31

I have a UITableView with 4 sections. Three of these sections have a header view.

The header view is a UITableViewCell, which I am de-queueing as a normal cell in the viewForHeaderInSection: delegate.

If I insert or delete a row from any section, the other tableview header cells disappear.

I'm assuming this has something to do with cell reuse, however the cells initially all appear on screen (all three headers appear onscreen at the same time).

I've tried reloading the other sections after the insert or delete, but that doesn't help.

Here's some code:

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    switch (section) {

        case kSectionCardInformation:
        case kSectionContactInformation:
        case kSectionTags: {

            static NSString *CellIdentifier = @"EditContactHeaderCell";
            EditContactHeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
            return cell;
        }

        default:
            return nil;
    }
}

And here is where I delete the row in the revelant section:

- (void)deleteTag:(CDTag *)tag {

    [self.tableView beginUpdates];

    NSMutableArray *objects = [self.sections objectAtIndex:kSectionTags];
    if ([objects containsObject:tag]) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[objects indexOfObject:tag] inSection:kSectionTags];
        [objects removeObject:tag];

        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationMiddle];

        [self.contact deleteTag:tag];
    }

    [self.tableView endUpdates];
}

Any help, greatly appreciated.

Khawar Ali
  • 3,462
  • 4
  • 27
  • 55
theDuncs
  • 4,649
  • 4
  • 39
  • 63
  • 1
    I have found that using a view that is not a subclass of UITableViewCell fixes the issue. But I don't understand why. – louissmr May 02 '15 at 07:49

7 Answers7

25

Just wrap your UITableViewCell into a UIView.

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

switch (section) {

    case kSectionCardInformation:
    case kSectionContactInformation:
    case kSectionTags: {

        static NSString *CellIdentifier = @"EditContactHeaderCell";
        EditContactHeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        UIView * view = [[[UIView alloc] init] autorelease];
        [view addSubview:cell];
        //setup the view's frame: fixed, autolayout, ...
        return view;
    }

    default:
        return nil;
}

}

louissmr
  • 748
  • 8
  • 18
  • This works. Any idea why we need to wrap it in a ```UIView```? – KingPolygon Jan 17 '16 at 21:05
  • I guess that the table view explores its subviews and if a header view is a cell, somehow it is processed as a cell. – louissmr Jan 18 '16 at 16:52
  • Interesting. I was having the same problem with a custom `UIView` subclass so maybe it uses an if statement with `kindOf:` instead of `memberOf:` – KingPolygon Jan 18 '16 at 23:15
19

Returning the contentView of the cell worked nicely for me. No need to wrap the cell in a UIView.

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerCell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell")
    return cell.contentView
}
dsunku
  • 503
  • 5
  • 9
  • 1
    Worked perfect and resizes too (Which was my problem trying to do the "put the cell in a view" solution) – Will May 02 '19 at 14:07
  • After trying a few answers, this one is the most straightforward, but it assumes you don't have any buttons in your section header – craft Sep 26 '19 at 23:10
  • @craft i am using same approach but button is there in my header cell, which get disappears, when all rows deleted and button is tapped again to add more row. – iPhone 7 Feb 04 '20 at 13:25
  • @iPhone7 - ahh, yes, I may have been mistaken. Are you saying it still works even if there's a button? It seems like it should, looking at this answer again. If so, I'll delete my comment. Thank you – craft Feb 04 '20 at 14:52
  • @craft No, i was saying that it didn't work when there's a button. And the cause diagnosed is, due to TapGesture for dismissing keyboard interfering with the functionality. In my case complete header view gets disappear when button on it tapped. Hope i made my point clear to you? – iPhone 7 Feb 05 '20 at 06:17
  • @iphone7 ok, thanks for the additional context! Leaving my original comment as is. – craft Feb 05 '20 at 15:49
  • Why does this work ? – S.S.D Jul 03 '23 at 08:59
18

Adding comment since I had the same problem... The problem was that I too was using a UITableViewCell as a header instead of using an UITableViewHeaderFooterView.

To fix the problem:

  1. Create a nib file and make your custom header there instead of within the UITableView on your storyboard.
  2. Link it to your custom UITableViewHeaderFooterView class
  3. Register this nib inside your viewDidLoad function

    override func viewDidLoad() {
        self.tableView.rowHeight = UITableViewAutomaticDimension
    
        self.tableView.register(UINib(nibName: "NibFileName", bundle: nil), forHeaderFooterViewReuseIdentifier: "CustomViewHeaderView")
    }
    
  4. Dequeue your reusable HeaderFooterView from the viewForHeaderInSection function:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier:"CustomViewHeaderView") as! CustomViewHeaderView
    
        return cell
    }
    

This fixed my disappearing Header. I hope it can help others.

Francois Nadeau
  • 7,023
  • 2
  • 49
  • 58
6

For Swift 4,5

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

    let containerView = UIView()
    guard let headerCell = tableView.dequeueReusableCell(withIdentifier: "MyHeaderView") as? MyHeaderView else { fatalError(" Failed to load MyHeaderView") }
    containerView.addSubview(headerCell)
    return containerView
}
Alejandro Vargas
  • 1,327
  • 12
  • 26
2

Correct way to resolve this problem is to use UITableViewHeaderFooterView instead of using UITableViewCell for header section. Like @Francois Nadeau answered.

For more detail on how to use UITableViewHeaderFooterView for header section please see this answer: https://stackoverflow.com/a/36931047

Anwaar Malik
  • 121
  • 3
  • 7
2

Do not return your FooterCell or HeaderCell , or your reusable identifier. Return the reuseIdentifier.contentView. For me it's:

return headerCell!.contentView.

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerCell = tableView.dequeueReusableHeaderFooterView(withIdentifier:"CustomViewHeaderView") as! CustomViewHeaderView

    return headerCell.contentView
}
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
Diaa SAlAm
  • 366
  • 1
  • 4
  • 7
-1

I dont know why are you are using tableview cell for header, but my assumption is that

You have missed the break statement

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    switch (section) {

        case kSectionCardInformation:
            break;
        case kSectionContactInformation:
            break;
        case kSectionTags: {

            static NSString *CellIdentifier = @"EditContactHeaderCell";
            EditContactHeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
            return cell;
        }

        default:
            return nil;
    }
}

Check nil condition before returning the cell, if it was nil then create the cell.

            static NSString *CellIdentifier = @"EditContactHeaderCell";
            EditContactHeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

            return cell ?: createdCellNewly;

Updates:

Then check your numberOfSectionsInTableView:(UITableView *)tableView method returns the proper count.

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
         // returning count is correct?
    }

Check and set the tableview's delegate & datasource.

Tim
  • 597
  • 1
  • 5
  • 15
  • I don't need the break statements - those three case statements all apply for creating the cell. In fact, the function doesn't get called again after I insert or delete a row (only if the header drops off screen and reappears). – theDuncs May 02 '14 at 10:02
  • Vijay. Thanks for your help, but all of the above is covered correctly. – theDuncs May 02 '14 at 10:19