0

Making a header in a UICollectionView is turning out to be much harder then in a table.

I subclassed the UICollectionViewReusableView, registered it's class in the UICollectionView, implemented collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath. In my header view I have two UITextField's and one UITextView. I need to have access to their delegate methods (editing began, finished...). Using this example I saw that you can do:

UICollectionReusableView *footer = (UICollectionReusableView*)[self.collectionView viewWithTag:999];
UILabel *footerLabel = (UILabel*)[footer viewWithTag:100];

But creating that many variables for every delegate method seems wrong. I need to be able to set the text in those text fields and text view. Is there a better way of doing this?

//...viewDidLoad
UICollectionView *images = [[UICollectionView alloc]initWithFrame:self.view.bounds collectionViewLayout:layout];
[images registerClass:[ImagesCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[images registerClass:[HeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"headerView"];
//images data source and delegate

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
    if (kind == UICollectionElementKindSectionHeader) {
        HeaderView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"headerView" forIndexPath:indexPath];
        if (!headerView)
            headerView = [[HeaderView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 180)];
            headerView.delegate = self;
        return headerView;
    }
    return nil;
}

And then I use casting with the tags to get the view. Is there any other way to update the individual views inside the header?

UPDATE:

HeaderView.h

@protocol HeaderViewDelegate <NSObject>

- (void)headerView:(id)view didBeginEditingFullName:(UITextField *)fullNameTextField;
- (void)headerView:(id)view didEndEditingFullName:(UITextField *)fullNameTextField;
- (void)headerView:(id)view didBeginEditingBreed:(UITextField *)breedTextField;
- (void)headerView:(id)view didEndEditingBreed:(UITextField *)breedTextField;
- (void)headerView:(id)view didBeginEditingDescription:(UITextView *)descriptionTextView;
- (void)headerView:(id)view didEndEditingDescription:(UITextView *)descriptionTextView;

@end

@interface HeaderView : UICollectionReusableView {
    id <HeaderViewDelegate> delegate;
}
@property (nonatomic, weak) id <HeaderViewDelegate> delegate;

This is how I set up the HeaderView.h...had to use id instead of (HeaderView *) in the methods...I was getting an error.

This is what I had in my PersonalViewController.m page before:

- (void)textFieldDidBeginEditing:(UITextField *)textField{
    if (textField == _fullName) {

    }else if (....)
}

Now that _fullName is inside the supplementary view, I can't access it. This is what I was trying to get to...

Changing it to this:

- (void)headerView:(HeaderView *)view didBeginEditingFullName:(UITextField *)fullName{
    NSLog (@"here");
}

No log output. I set the delegates for the views in HeaderView.m. Dammit, what am I not getting here?

UPDATE:

HeaderView.h is the same as before. I added the textfield and textview delegates to it. HeaderView.m:

@implementation HeaderView

@synthesize delegate=_delegate;

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        UITextField *fullName = [[UITextField alloc]initWithFrame:CGRectMake(5, 100, 120, 21)];
        fullName.tag = 4;
        fullName.delegate = self;
        _fullName = fullName;
        [self addSubview:_fullName];
    }
}

PersonalViewController.m is the same. viewForSupplementaryElementOfKind is still the same as before...setting headerView.delegate = self;. In it I'm calling

- (void)headerView:(HeaderView *)view didBeginEditingFullName:(UITextField *)fullName{
    NSLog(@"here");
}

Should I be calling it inside the HeaderView.m? I need to call it from PersonalViewController.m...

Community
  • 1
  • 1
denikov
  • 877
  • 2
  • 17
  • 35
  • How many supplementary views of the same kind would you be displaying on the screen? – duci9y Jul 06 '14 at 12:13
  • 1
    use `@class HeaderView;` right above your `@protocol` line, so that you can replace `(id)view` with `(HeaderView*)view` – kas-kad Jul 06 '14 at 13:00
  • 1
    in your PersonalViewController you must implement HeaderViewDelegate protocol methods, not `textFieldDidBeginEditing:(UITextField *)textField` – kas-kad Jul 06 '14 at 13:19
  • @purrrminator Yes, I understand I had to change it to the custom protocol methods and I did change it...updated the question... – denikov Jul 06 '14 at 13:27
  • Still I don't see where you call `-headerView:didBeginEditingFullName:` – kas-kad Jul 06 '14 at 13:34
  • Ok, one last try...I'll update my question with the parts which we talked about...maybe you can see where my mistake is. – denikov Jul 06 '14 at 13:38
  • @purrrminator I updated the question...not sure what else I can show to help... – denikov Jul 06 '14 at 13:47
  • 1
    If you set your HeaderView as a delegate of your textField so why don't you implement any delegation method? You have to implemement `-textFieldDidBeginEditing:` in your HeaderView. Inside the method you have to call headerView's delegate's method `-(void)headerView:(id)view didBeginEditingFullName:`. Sorry I'm starting wasting my time here. Your question is growing but my initial answer is not marked yet. – kas-kad Jul 06 '14 at 14:00
  • @purrrminator lol Yeah yeah, I got it to work. The basic textfield delegate method needs to get called in the HeaderView.m, the custom delegate methods are called in PersonalViewController.m. `[self.delegate headerView:self didBeginEditingFullName:textField]` is the part that confused me. Thanks for being PATIENT with me :) – denikov Jul 06 '14 at 14:07
  • I'm sure you know much more about the delegation pattern now ) – kas-kad Jul 06 '14 at 14:09
  • @purrrminator Eh...sort of. Still confused about `[self.delegate headerView:self didBeginEditingFullName:textField]`. Not sure what 'literally' it's saying. Next step is to figure out your advice about distinguishing the cell's subviews. More fun! – denikov Jul 06 '14 at 14:19

1 Answers1

1

You must understand that reusable cells are destroyed and reinitialized each time you scroll off the cell.

I would create a subclass for your supplementary view (as you already did), making all texfields as properties (you did this too). Also i'd create a protocol for your custom supplementary cell like that:

@protocol HeaderViewDelegate <NSObject>
    -(void)headerView:(HeaderView*)view didBeginEditingFirstTextField:(UITextField*)firstTextField;
    -(void)headerView:(HeaderView*)view didBeginEditingSecondTextField:(UITextField*)secondTextField;
    -(void)headerView:(HeaderView*)view didBeginEditingTextView:(UITextView*)textView;
@end

and then implement a delegtion pattern:

headerView.delegate = self;

and so on.

Tip: If you want to distinguish cell's subviews and have an access to them by indexPath you still can use tags without any extra variables, simply init each tag on this formula:

tag = indexpath.hash + subviewIndex

subviewIndex - is just an order number that you set manually, it must be unique for each subview of your cell. (In your example first textfield is 1, second is 2, textview is 3).

kas-kad
  • 3,736
  • 1
  • 26
  • 45
  • Thanks for your answer...I've never done a delegate on a cell like this...let me try it now and get back to you. – denikov Jul 06 '14 at 12:35
  • @denikov don't forget to set your `headerView` as a delegate for those textFields. – kas-kad Jul 06 '14 at 12:44
  • I updated my answer to that...no errors. Question...where do I set the delegate for those views...inside the main .m class? – denikov Jul 06 '14 at 12:55
  • 1
    you set textfields delegates right after initialization/adding as a subview inside HeaderView.m `fullnameTextField.delegte = self; [self addSubview:fullnameTextField];` – kas-kad Jul 06 '14 at 12:58
  • I get a warning `Assigning to 'id' from incompatible type 'HeaderView *_strong'`. Does that mean I should add the the text field delegates to HeaderView.h? – denikov Jul 06 '14 at 13:02
  • 1
    You have to modify your headerView to conform protocols like this `@interface HeaderView : UICollectionReusableView ` – kas-kad Jul 06 '14 at 13:03
  • Ok, now I added a log statement into `didBeginEditingFullName...`...no output. I tried adding it to the HeaderView.m also, no log output. Sorry for all these questions... – denikov Jul 06 '14 at 13:09
  • Because you don't call the cells delegate method from textfield delegate method. As I can tell, you have some problems implementing standart patternt of delegation :) – kas-kad Jul 06 '14 at 13:12
  • The basic `UITextFieldDelegate` I understand. When you say "call the cells delegate method from textfield delegate method", that part I do not understand... – denikov Jul 06 '14 at 13:15
  • 1
    if you implement delegation for textfield in your HeaderView `-(void)textFieldDidBeginEditing:(UITextField *)textField { /*here you must call [self.delegate headerView:self didBeginEditingFullName:textField]>*/ }` – kas-kad Jul 06 '14 at 13:18
  • Yeah, that part is new to me :) – denikov Jul 06 '14 at 13:19
  • One small question, if you feel up to it. You say to distinguish the cell's subviews "init the tag with a formula", using indexPath. If I init it in HeaderView, how can I use indexPath? – denikov Jul 06 '14 at 15:49
  • @denikov I don't now what kind of task you are working on. But at this moment you have one delegate (your ViewController) which is delegated to work with many headers. Right? – kas-kad Jul 06 '14 at 18:02
  • It's just one header with editable information about the user (text fields and text view). I was just looking for an easy way to access those views inside the header (for example, setting the text in those views when some data is downloaded from server). You solution works but only if I make custom protocol methods for those views. I thought there might be a simpler way then using delegates. Everything is ok, good examples to learn from that you provided. – denikov Jul 07 '14 at 14:34