Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView -headerViewForSection returns (null)

I have a UITableView with 2 sections. Each with it's own headerView.
I have created a custom headerView via the -viewForHeaderInSection: method.
Later, I plan to modify it a bit so I need to use the viewForHeader method but I am unable to access the headerView and it's subViews.

As a simple example, I'm trying to NSLog the viewForHeader object in the -didSelectRowAtIndexPath: but I get a (null) result.

Sample Code:

-(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 75;
}

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{    
    UIView *myHeader = [[UIView alloc] init];
    switch (section) {
        case 0:
            [myHeader setBackgroundColor:[UIColor greenColor]];
            break;
        case 1:
            [myHeader setBackgroundColor:[UIColor redColor]];
            break;
        default:
            break;
    }

    UILabel *myLabel = [[UILabel alloc] init];
    [myLabel setFrame:CGRectMake(10, 0, 100, 30)];
    [myLabel setTag:101];
    [myLabel setBackgroundColor:[UIColor clearColor]];
    [myLabel setText:[NSString stringWithFormat:@"Section: %d",section]];

    [myHeader addSubview:myLabel];    
    return myHeader;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewHeaderFooterView *testView = [self.tableView headerViewForSection:indexPath.section];
    NSLog(@"%@",testView);  //displays (null)
}

Do I need to create the custom UIView xib as the headerView? (because as per a similar question and as per the docs")

To make the table view aware of your header or footer view, you need to register it.
You do this using the registerNib:forCellReuseIdentifier: or
registerClass:forCellReuseIdentifier: method of UITableView.

like image 834
staticVoidMan Avatar asked Nov 25 '13 08:11

staticVoidMan


2 Answers

METHOD 1:

Ok, after some trial and error, I finally solved my own dilemma.
I did the headerView just as I would do a cell.

For a cell we would take UITableViewCell and use dequeueReusableCellWithIdentifier
while...
For a Header/Footer, we will take UITableViewHeaderFooterView and use the dequeueReusableHeaderFooterViewWithIdentifier method.

The rest is pretty much the same concept as a cell.

Prerequisites:

  1. Set header height to 40
  2. Set number of sections to 2 or more
  3. Set number of rows per section to be atleast 1
  4. iOS6+ (UITableViewHeaderFooterView won't work with iOS5 and below)

First Approach:

Creating and using the default UITableViewHeaderFooterView within -viewForHeaderInSection: method:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *HeaderIdentifier = @"header";

    UITableViewHeaderFooterView *myHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:HeaderIdentifier];
    if(!myHeader) {
        myHeader = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:HeaderIdentifier];
    }

    UIButton *btnUp = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [btnUp setTag:101];
    [btnUp setTitle:@"-" forState:UIControlStateNormal];
    [btnUp setFrame:CGRectMake(tableView.frame.size.width - 35, 5, 30, 30)];
    [myHeader addSubview:btnUp];

    [myHeader.textLabel setText:[NSString stringWithFormat:@"Section: %d",section]];

    [myHeader setFrame:CGRectMake(0, 0, tableView.frame.size.width, 50)];
    return myHeader;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewHeaderFooterView *theHeaderView = [tableView headerViewForSection:indexPath.section];
    NSLog(@"%@",theHeaderView); // -- great! ... not (null) anymore

    UIButton *theButton = (UIButton *)[theHeaderView viewWithTag:101];
    [theButton setTitle:@"+" forState:UIControlStateNormal];
}

Second Approach:

Using a custom UITableViewHeaderFooterView subclass:

  1. Created a UITableViewHeaderFooterView subclass and named it CustomHeaderView
  2. Created a View interface nib file for the class
  3. In the xib, selected the View & in it's Identity Inspector
    • Specified the Custom Class as CustomHeaderView
  4. Made properties, synthesized and connected them in the xib
    • @property (strong, nonatomic) IBOutlet UILabel *lblSomething;
    • @property (strong, nonatomic) IBOutlet UIButton *btnSomething;

Modified the -viewForHeaderInSection: & -didSelectRowAtIndexPath: as:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *HeaderIdentifier = @"header";

    CustomHeaderView *myHeader = [tableView dequeueReusableHeaderFooterViewWithIdentifier:HeaderIdentifier];
    if(!myHeader) {
    //    [tableView registerClass:[CustomHeaderView class] forHeaderFooterViewReuseIdentifier:HeaderIdentifier];
        myHeader = [[[NSBundle mainBundle] loadNibNamed:@"CustomHeaderView"
                                                  owner:self
                                                options:nil] objectAtIndex:0];
    }

    [myHeader.btnSomething setTitle:@"-" forState:UIControlStateNormal];
    [myHeader.lblSomething setText:[NSString stringWithFormat:@"Section: %d",section]];

    return myHeader;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomHeaderView *theHeaderView = (CustomHeaderView*)[tableView headerViewForSection:indexPath.section];
    NSLog(@"%@",theHeaderView);

    [theHeaderView.lblSomething setAlpha:theHeaderView.lblSomething.alpha-0.1];
    [theHeaderView.btnSomething setTitle:@"+" forState:UIControlStateNormal];
}

PS: The issue with UITableViewHeaderFooterView is that it is iOS6+ only and if, for any reason, your header is/must be a UIView, then see the next method


METHOD 2:

Using a simple UIView:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    UIView *vwHeader = [[UIView alloc] init];
    [vwHeader setTag:200 + section]; //[1] first method

    switch (section) {
        case 0:
            [vwHeader setBackgroundColor:[UIColor greenColor]];
            break;
        case 1:
            [vwHeader setBackgroundColor:[UIColor redColor]];
            break;
        default:
            break;
    }

    UILabel *lblTitle = [[UILabel alloc] init];
    [lblTitle setFrame:CGRectMake(10, 0, 100, 30)];
    [lblTitle setTag:100 + section]; //[2] alternative method
    [lblTitle setBackgroundColor:[UIColor clearColor]];
    [lblTitle setText:[NSString stringWithFormat:@"Section: %d",section]];

    [vwHeader addSubview:lblTitle];
    return vwHeader;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    UIView *vwTest = [self.tableView viewWithTag:200 + indexPath.section]; //[1]
    NSLog(@"[1] : %@",vwTest);

    //or

    UILabel *lblTest = (UILabel *)[self.tableView viewWithTag:100 + indexPath.section]; //[2]
    NSLog(@"%@",lblTest.text);
    UIView *vwTestForSuperview = lblTest.superview;
    NSLog(@"[2] : %@",vwTestForSuperview);
}

PS: I know this code doesn't serve any great purpose but this is just an example for others.

like image 200
staticVoidMan Avatar answered Nov 03 '22 00:11

staticVoidMan


I think your problem can be solved easily enough:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{    
UITableViewHeaderFooterView *myHeader = [[UITableViewHeaderFooterView alloc] init];
switch (section) {
    case 0:
        [myHeader setBackgroundColor:[UIColor greenColor]];
        break;
    case 1:
        [myHeader setBackgroundColor:[UIColor redColor]];
        break;
    default:
        break;
}

UILabel *myLabel = [[UILabel alloc] init];
[myLabel setFrame:CGRectMake(10, 0, 100, 30)];
[myLabel setTag:101];
[myLabel setBackgroundColor:[UIColor clearColor]];
[myLabel setText:[NSString stringWithFormat:@"Section: %d",section]];

[myHeader addSubview:myLabel];    
return myHeader;

}

Basically, you need to use UITableViewHeaderFooterView class to return from viewForHeaderInSection callback. After that, calling headerViewForSection on your table view will be returning you valid instances of objects instead of nil.

like image 29
CocoaAficionado Avatar answered Nov 03 '22 01:11

CocoaAficionado