17

I'm trying to change the header title for a section in a UITableView when a cell from that section is selected. tableView:titleForHeaderInSection is triggered by the application, so that doesn't help. I can call reloadData, but the performance suffers because the app has to reload all the visible cells. I also tried to use a custom header, but that also results in some performance problems.

Is there any way to get a handle to the UILabel that the default header view uses and change its text manually?

Cœur
  • 37,241
  • 25
  • 195
  • 267

9 Answers9

24

Calling [tableView endUpdates] may provide the desired results without too much of a performance hit.

[self.tableView beginUpdates];
[self.tableView endUpdates];

// forces the tableView to ask its delegate/datasource the following:
//   numberOfSectionsInTableView:
//   tableView:titleForHeaderInSection:
//   tableView:titleForFooterInSection:
//   tableView:viewForHeaderInSection:
//   tableView:viewForFooterInSection:
//   tableView:heightForHeaderInSection:
//   tableView:heightForFooterInSection:
//   tableView:numberOfRowsInSection:
Cœur
  • 37,241
  • 25
  • 195
  • 267
Scott McCammon
  • 1,865
  • 16
  • 10
  • 3
    In iOS10/11 (swift) I have noticed `tableView:viewForHeaderInSection:` and `tableView:viewForFooterInSection:` are not called so you need to change the view yourself – Casper Zandbergen Nov 21 '17 at 14:26
17

with:

[self.tableView headerViewForSection:i]

you could get the view for Section i and then "update" it manually

this even works if your view is just the auto-generated label, but you will have to resize it yourself. so if you try to:

[self.tableView headerViewForSection:i].textLabel.text = [self tableView:self.tableView titleForHeaderInSection:i];

you will set the text, but you would not set the label-size. You can get the needed size from NSString to set it yourself:

[label.text sizeWithFont:label.font];
Dr. Azrael Tod
  • 499
  • 5
  • 16
15

There doesn't appear to be any standard API for accessing the system-provided section header view. Have you tried the more targeted reloadSections:withRowAnimation to get UIKit to display the new header text?

What kind of performance issues were you seeing with custom section header views? I doubt that the standard one is much more than just a UILabel.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Sixten Otto
  • 14,816
  • 3
  • 48
  • 60
  • It seems like I might have to use reloadSections, even though it's nearly as slow as reloading the whole table (for my app at least). I have been foiled at every turn by the Cocoa API. I tried using a circular queue to reuse a set of UILabel's and returning them in viewForHeader, but viewForHeader is called for so many cells (even ones that aren't visible!), that I needed to create a separate UILabel for every cell, which is just plain bad. Argh! –  Oct 24 '09 at 00:46
4

You can set the title for the section header label directly. For example, to set the title of section zero:

UITableViewHeaderFooterView *sectionZeroHeader = [self.tableView headerViewForSection:0];
NSString *sectionZeroLabel = @"Section Zero";
[sectionZeroHeader.textLabel setText:[sectionZeroLabel uppercaseString]];
[sectionZeroHeader setNeedsLayout];

Be sure and tell the section header view it needs layout, otherwise the new text may be truncated. Also, section labels are usually all uppercase.

Ken Ryall
  • 41
  • 2
1

Since a UITableView does not enqueue and dequeue section header views for reuse, you might as well see if it's feasible to store all the section header views in memory. Note you'll have to create your own section header views with the background and etc., to do this, but it allows you a bit more flexibility and capability.

You could also try tagging the section header views (also requires you to create your own section header views) and just grab them from the tableview as needed.

David Liu
  • 9,426
  • 5
  • 40
  • 63
0

One of the solution would be to manage external array for multiple section headers containing label references and update them externally.

mangesh
  • 574
  • 4
  • 16
0

This is a WAG, and I can think of lots of reasons why it might not work, but couldn't you iterate through the subviews, and find the one you are looking for? E.g.

for (UIView *v in self.tableView.subviews) {
    // ... is this the one?
}
Clay Bridges
  • 11,602
  • 10
  • 68
  • 118
0

For completeness, the method we are all looking for is this private API named exactly what you'd expect:

-(void)_reloadSectionHeaderFooters:withRowAnimation:

eg:

[tableView _reloadSectionHeaderFooters:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationAutomatic]

I do not recommend using this, however.

zeroimpl
  • 2,746
  • 22
  • 19
0

If you want to update only one section best way is tableView.headerView. If header is not visible it returns nil, so it doesn't load extra headers.

if let header = tableView.headerView(forSection: i) {
    header.textLabel!.text = "new title"
    header.setNeedLayout()
}

If you want to update all visible section headers it is better to set header tag as section before view is displayed and enumerate subviews when needed:

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
    view.tag = section
    // additional customization
}

func udpateVisibleSectionHeaders() {
    for subview in tableView.subviews {
        if let header = subview as? UITableViewHeaderFooterView {
            let section = header.tag
            header.textLabel!.text = "new title"
            header.setNeedsLayout()
        }
    }
}

Don't forget to call setNeedsLayout or label can be truncated.

Alexander Danilov
  • 3,038
  • 1
  • 30
  • 35