UITableView : viewForHeaderInSection: not called during reloadData:
Asked Answered
C

16

132

I've set up the tableview with correct delegate and datasource linkages.. the reloadData method calls the datasource and the delegate methods except for viewForHeaderInSection:.

Why is that so?

Chiao answered 25/2, 2013 at 23:44 Comment(2)
Is the heightForHeaderInSection: implemented?Echols
Have you set a value for the TableView's sectionHeaderHeight?Voltage
E
266

The use of tableView:viewForHeaderInSection: requires that you also implement tableView:heightForHeaderInSection:. This should return an appropriate non-zero height for the header. Also make sure you do not also implement the tableView:titleForHeaderInSection:. You should only use one or the other (viewForHeader or titleForHeader).

Earreach answered 25/2, 2013 at 23:51 Comment(6)
Make sure there isn't a typo in the method's signature. One wrong letter will mean it won't be called. Check the case too. Also make sure you are returning 0 from numberOfSections.Earreach
everything is correct and compiles correctly.. the issue I wanted to understand is around the timing of when the method is called .. tableView:viewForHeaderInSection is called when the table is just about to be displayed and not as part of the synch execution of [tableview reloadData]Chiao
@maddy OMG Thank you, it's so stupid of me i created my instances but I didn't append to my arrayMoody
You can absolutely have both. viewForHeaderInSection: will have precedence over titleForHeaderInSection: The only requirement is that you set estimatedSectionHeaderHeight on your table view with something different than 0, otherwise viewForHeaderInSection: will never get calledBeghtol
Adding to @romrom's comment: if you have implemented both titleForHeaderInSection: and viewForHeaderInSection: and the view returned from the latter is a subclass of UITableViewHeaderFooterView then its textLabel.text is automatically set to the all-caps version of the titleForHeaderInSection: string. To prevent this behavior, either don't implement titleForHeaderInSection: or use a custom label instead of the inherited textLabel.Ginseng
I've just encountered a situation where only one of the headers isn't called. The heightForHeaderInSection is called for all sections, but the viewForHeaderInSection is not called for one of the sections. Which one? The one where the included textfield was just edited. Why does it need to be called? Because the editing could cause the section sort to change. When the data is updated, it is sorted, so I want to redisplay ALL sections, including the section just edited. Weirdly, that is the one that is skipped even though height is queried.Hebraist
U
43

The trick is that those two methods belong to different UITableView protocols: tableView:titleForHeaderInSection: is a UITableViewDataSource protocol method, where tableView:viewForHeaderInSection belongs to UITableViewDelegate.

That means:

  • If you implement the methods but assign yourself only as the dataSource for the UITableView, your tableView:viewForHeaderInSection implementation will be ignored.

  • tableView:viewForHeaderInSection has a higher priority. If you implement both of the methods and assign yourself as both the dataSource and the delegate for the UITableView, you will return the views for section headers but your tableView:titleForHeaderInSection: will be ignored.

I have also tried removing tableView:heightForHeaderInSection:; it worked fine and didn't seem to affect the procedures above. But the documentation says that it is required for the tableView:viewForHeaderInSection to work correctly; so to be safe it is wise to implement this, as well.

Understood answered 12/6, 2014 at 7:55 Comment(2)
You made my day!!! Forgot to assign UITableViewDelegate to self, because I thought, that tableView:viewForHeaderInSection is a UITableViewDataSource method. Thank you!Aeciospore
"tableView:viewForHeaderInSection" is not vital. What's vital is you somehow return a height. You could achieve that through either 1. estimate or 2. a hardcoded value or 3. a titleForHeader which has an intrinsic size. The intrinsic size is calculated based on the Font Family and size.Pindus
V
29

@rmaddy has misstated the rule, twice: in reality, tableView:viewForHeaderInSection: does not require that you also implement tableView:heightForHeaderInSection:, and also it is perfectly fine to call both titleForHeader and viewForHeader. I will state the rule correctly just for the record:

The rule is simply that viewForHeader will not be called unless you somehow give the header a height. You can do this in any combination of three ways:

  • Implement tableView:heightForHeaderInSection:.

  • Set the table's sectionHeaderHeight.

  • Call titleForHeader (this somehow gives the header a default height if it doesn't otherwise have one).

If you do none of those things, you'll have no headers and viewForHeader won't be called. That's because without a height, the runtime won't know how to resize the view, so it doesn't bother to ask for one.

Vachell answered 2/10, 2014 at 15:14 Comment(12)
From the docs for tableView:viewForHeaderInSection:: " This method only works correctly when tableView:heightForHeaderInSection: is also implemented.".Earreach
Fine. What the docs say, they say. Now experiment. The facts are as I have stated.Vachell
And how can you have both titleForHeaderInSection and viewForHeaderInSection? The table view will only call one of the two (I forget which gets precedence at the moment).Earreach
Actually there is one more piece of the puzzle, which is that sometimes viewForHeader is called without any of those three ways of assigning a height. I have had this happen, where my viewForHeader was called and headers showed up just fine, until one day, with no change on my part, they didn't. That's when I started experimenting to discover what the minimal requirements are for viewForHeader to be called. And now I know. And now so do you.Vachell
If both titleForHeaderInSection and viewForHeaderInSection are implemented, and assuming viewForHeaderInSection returns a UITableViewHeaderFooterView, they will both be called, and in fact this is a perfectly reasonable thing to do.Vachell
Now I tried to implement titleForHeaderInSection together with viewForHeaderInSection (whereas I only need the view). It works as matt stated but what should titleForHeaderInSection return? It only worked for me when returning an empty space. So it's still a compromise because I have to additionaly return things (height or space).Vertu
@matt, do you have any clue for the following #34665813Logo
@texas No, I don't do xamarin. Adding another level of indirection on top of the Cocoa frameworks would just make my head explode. :)Vachell
:-) Just ignore about xamarin, my question is simply that my implementation always calls TitleHeader but not calling ViewHeight and ViewHeader. What might be reason?Logo
Any reason you excluded estimatedSectionHeaderHeight from your three ways?Pindus
@Honey Look at the date on that answer.Vachell
Ouch/hah. Didn't know. You have look at the date I started developing :) but I guess adding that to the answer won't bother.Pindus
K
24

Giving estimatedSectionHeaderHeight and sectionHeaderHeight values fixed my problem. e.g., self.tableView.estimatedSectionHeaderHeight = 100 self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension

Koziol answered 8/3, 2017 at 21:22 Comment(3)
My issue started after upgrading to Swift 3.1. This solution fixed it.Lustful
@pbuchheit Apple Docs says it is available from iOS 7.0+, please have a look here, developer.apple.com/documentation/uikit/uitableview/…Koziol
@Sharukh Mastan Looks like you are correct. For some reason I was getting a warning when I tried using that property, but it went away after doing a clean build.Cabinda
M
8

Going off rmaddy 's answer, I was trying to hide the Header view and was returning 0.0f for "tableView:heightForHeaderInSection" and a 0 height View from tableView:viewForHeaderInSection .

After changing from return 1.0f to return 0.0f in tableView:heightForHeaderInSection, the delegate method tableView:viewForHeaderInSection was indeed called.

Turns out my desired effect works without having to use "tableView:heightForHeaderInSection"; but this may be useful to others who are having an issue getting "tableView:heightForHeaderInSection" delegate method called.

Mcgann answered 5/10, 2015 at 21:56 Comment(0)
G
7

You should implement tableView:heightForHeaderInSection: and set the height for the header >0.

This delegate method goes along with the viewForHeaderInSection: method.

I hope this helps.

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
         return 40;
}
Goalie answered 20/2, 2015 at 10:57 Comment(1)
Have you tested that your answer actually works? Becaus eif you read carefully then it's stated that initially the viewForHeaderInSection is called. It's not called only when the table is reloaded!Lilith
B
6

It's worth briefly noting that if your implementation of tableView:heightForHeaderInSection: returns UITableViewAutomaticDimension, then tableView:viewForHeaderInSection: will not be called.

UITableViewAutomaticDimension assumes that a standard UITableViewHeaderFooterView will be used that is populated with the delegate method tableView:titleForHeaderInSection:.

From comments in the UITableView.h:

Returning this value from tableView:heightForHeaderInSection: or tableView:heightForFooterInSection: results in a height that fits the value returned from tableView:titleForHeaderInSection: or tableView:titleForFooterInSection: if the title is not nil.

Beekeeper answered 24/7, 2015 at 11:41 Comment(2)
if you set estimatedSectionHeaderHeightto some value, tableView:viewForHeaderInSection will be called (similarly to how auto dimensions for rows works)Spangle
Interesting, thanks. Back at 7.1 this subtlety of estimated height was important for cells, so might have been the case for headers too – but it's not terribly relevant now!Beekeeper
B
4

I've just had an issue with headers not showing for iOS 7.1, but working fine with later releases I have tested, explicitly with 8.1 and 8.4.

For the exact same code, 7.1 was not calling any of the section header delegate methods at all, including: tableView:heightForHeaderInSection: and tableView:viewForHeaderInSection:.

After experimentation, I found that removing this line from my viewDidLoad made headers re-appear for 7.1 and did not impact other versions I tested:

// _Removing_ this line _fixed_ headers on 7.1
self.tableView.estimatedSectionHeaderHeight = 80;

… so, there seems to be some kind of conflict there for 7.1, at least.

Beekeeper answered 22/7, 2015 at 8:56 Comment(0)
F
4

Same issue occured with me but as I was using automatic height calculation from xCode 9, I cannot give any explicit height value as mentioned above. After some experimentation I got solution, we have to override this method as,

-(CGFloat)tableView:(UITableView *)tableView 
         estimatedHeightForHeaderInSection:(NSInteger)section
{
      return 44.0f;
}

Although I have checked both options

  1. Automatic Height Calculation
  2. Automatic Estimated Height Calculation

from storyboard as apple says, but still I got this weird error.

Please Note: This Error was shown only on IOS-10 version not on IOS-11 version. Maybe it's a bug from xCode. Thanks

Familiarize answered 23/11, 2017 at 7:30 Comment(0)
A
1

The reason why viewForHeaderInSection does not get called is for one of two reasons:

Either you did not set up your UITableViewDelegate, or you set up your UITableViewDelegate incorrectly.

Ataxia answered 5/12, 2019 at 20:22 Comment(0)
P
0

Here's what I've found (Swift 4) (thanks to this comment on another question)

Whether I used titleForHeaderInSection or viewForHeaderInSection - it wasn't that they weren't getting called when the tableview was scrolled and new cells were being loaded, but any font choices I made for the headerView's textLabel were only appearing on what was initially visible on load, and not as the table was scrolled.

The fix was willDisplayHeaderView:

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
    if let header = view as? UITableViewHeaderFooterView {
        header.textLabel?.font = UIFont(name: yourFont, size: 42)
    }
}
Pamela answered 22/9, 2018 at 14:39 Comment(0)
S
0

In my case I have created header view using UITableviewCell and returning the cell in viewForHeaderInSection like this

return cell

changed this to

return cell.contentView 

Worked for me.

Salomie answered 15/10, 2019 at 10:57 Comment(0)
M
0

In my case

viewForHeaderInSection

was implemented in a derived class far far away that was not bothering to daisy into superclass.

Motionless answered 30/10, 2019 at 13:4 Comment(0)
O
0

In my case it was cause I did not implement:

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
Orabelle answered 27/7, 2020 at 6:34 Comment(0)
D
-1

Sometimes setting tableview.delegate or datasource = nil in the viewWillAppear: or viewDidAppear: methods can cause this issue. Make sure not to do this...

Dipterocarpaceous answered 6/8, 2015 at 16:3 Comment(0)
B
-1

I had cut & paste the following two methods from a Swift 2 project into my Swift 3 project which were never called because in Swift 3 these methods must have "-" before the first parameter name.

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 44.0
}

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

    let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: B2BTrolleyHeaderFooterView.reuseIdentifier) as! B2BTrolleyHeaderFooterView        
    return headerView
}
Bergh answered 15/5, 2017 at 0:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.