Correct subclassing and reusing of UITableViewHeaderFooterView

3.4k views Asked by At

I have a UITableView where I have section headers that can be tapped to expand or collapse the section. In my particular example each section only has one row, which is either visible (section expanded) or hidden (section collapsed). As section header i'm using custom UITableViewHeaderFooterView - HeaderAccountView. I created *.xib file at Interface Builder , and set it custom class to my HeaderAccountView (still at IB field).

There are no any changes to init method or smth like this in my HeaderAccountView.h and HeaderAccountView.m files - only some functions to highlight self (selected section) etc.

in my main ViewController .m file

- (void)viewDidLoad
      {
      [super viewDidLoad];
      .........
      .........

       UITableView *tableView = (id)[self.view viewWithTag:1];
       UINib *nib= [UINib nibWithNibName:@"HeaderAccountView" bundle:nil];
       [tableView registerNib:nib forHeaderFooterViewReuseIdentifier:@"HeaderCell"];


      } 

and then

  - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
  {
         HeaderAccountView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"HeaderCell"];
      if (headerView==nil)
      {            headerView = [[HeaderAccountView alloc]
             initWithReuseIdentifier:@"HeaderCell"];
       }
   return headerView;
 }

when i'm running project everything going OK - sections load with needed data in it, when section receive tap - it highlights (like standard cell).

But when i'm scrolling away tableview to bottom for example from selected highlighted section, and this highlighted section already is not visible at view - that section that just appeared from bottom - already highlighted!

I understand that its because it creates new instance of my HeaderAccountView with property BOOL selected set to YES.

But I'm new to objective-c (and coding) and don't understand how to correct resolve this.

I tried to use prepareForReuse method of my custom UITableViewHeaderFooterView like this

  HeaderAccountView.m:
  -(void) prepareForReuse
  {
      self.selectedBackground.alpha = 0;
  }

It works better - but now i have another issue - when i returning to my first (truly) selected and highlighted section - it obviously don't highlight.

Thanks for any help and sorry if it elementary question.

2

There are 2 answers

1
Gougou On BEST ANSWER

You have to manually keep a list of your selected headers indexes.

Next, implement the method tableView:willDisplayHeaderView: in your view controller to refresh your header when it will be displayed.

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
    view.selectedBackground.alpha = ([_highlightedHeadersList containsObject:@(section)] ? 0.0f : 1.0f);
}    

And you have to add / remove indexes in _highlightedHeadersList.

1
rdelmar On

I've done this using the following. In the table view controller I created a property (NSInteger), and called it sectionForSelectedHeader. Set it to -1 initially so no section will be initially selected.

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    RDHeader  *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"Header"];
    header.tag = section;
    if (header.gestureRecognizers.count == 0) {
        UITapGestureRecognizer *tapper = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(headerSelected:)];
        [header addGestureRecognizer:tapper];
    }

    header.selected = (self.sectionForSelectedHeader == section)? 1 : 0;
    return header;
}

-(void)headerSelected:(UITapGestureRecognizer *) tapper {
    if ([(RDHeader *)tapper.view selected] != 1) {
        self.sectionForSelectedHeader = tapper.view.tag;
    }else{
        self.sectionForSelectedHeader = -1;
}

[self.tableView reloadData];

}

Then in my custom header subclass, I have a method setSelected: (selected is an NSInteger property in the RDHeader class) like this:

 -(void)setSelected:(NSInteger)selected {
    _selected = selected;
    if (selected) {
        self.contentView.backgroundColor = [UIColor orangeColor];
    }else{
        self.contentView.backgroundColor = [UIColor yellowColor];
    }
}