9

In iOS 11, my section headers always appear, regardless of whether the items are 0 or more.

On iOS 10 devices, my code works and sections disappear when item count is 0. But on iOS 11, the same code has no affect.

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    if sections[section].items.count > 0{
        return sections[section].title
    }else{
        return nil
    }
}
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080
  • 1
    Are you possibly implementing heightForHeaderInSection or viewForHeaderInSection? That could make your sections appear or not. Also apple docs say about titleForHeaderInSection "Return Value: A string to use as the title of the section header. If you return nil , the section will have no title." Doesn't say there will be no section, only no title. – Au Ris Oct 05 '17 at 21:10

8 Answers8

10

In iOS 11 if you implement only titleForHeaderInSection and return nil, you will not see a header view. But if you also implement viewForHeaderInSection, regardless of what you return, there will be a section.

This alone will not show a section header:

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return nil
}

This will show a section header with no title:

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    return nil
}

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

So both methods may return nil and the header will be visible. If only titleForHeaderInSection is implemented, no header shows up. That does seem to be a case only in iOS 11. Not sure if it's a bug or a way to force developers chose one method of the two. But the docs confirm this behaviour about titleForHeaderInSection:

"Return Value: A string to use as the title of the section header. If you return nil , the section will have no title."

So nothing about showing or not showing, this method only returns the string for the title. Which makes sense. But what does look like a bug is that returning nil in viewForHeaderInSection will show the section header.

Au Ris
  • 4,541
  • 2
  • 26
  • 53
5

To hide a section header for, say, section 0, implement the following method like so:

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if (section == 0) {
        return CGFloat.leastNormalMagnitude //Now section 0's header is hidden regardless of the new behaviour in iOS11.
    }

    return tableView.sectionHeaderHeight
}

This solution also works for grouped UITableViews, as discussed below.

Update: If you execute reloadSections(..), this solution causes an

NSException of type 'CALayerInvalidGeometry'

If you return 0 in the if statement however, this crash doesn't occur! :)

Therefore, I would say the best solution I have found (atleast for plain UITableViews) is:

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    if (section == 0) {
        return 0
    }

    return tableView.sectionHeaderHeight
}
shim
  • 9,289
  • 12
  • 69
  • 108
AppreciateIt
  • 696
  • 2
  • 8
  • 23
  • Returning 0 causes UITableView to use a default value. This is undocumented behavior. If you return a very small number, you effectively get a zero-height header. – AppreciateIt Oct 08 '17 at 18:48
  • 1
    That's not my experience. My experience is that returning 0 suppresses the header. – matt Oct 08 '17 at 18:49
  • It may have changed in a recent version of iOS, but this was definitely an issue in the past. It may also depend on whether you use a grouped UITableView or not. https://stackoverflow.com/questions/17699831/how-to-change-height-of-grouped-uitableview-header – AppreciateIt Oct 08 '17 at 18:53
  • Okay, I definitely am not talking about a grouped table view. – matt Oct 08 '17 at 18:58
  • Well, that is a common type so it may be relevant to others. – AppreciateIt Oct 08 '17 at 19:03
  • `CGFloat.leastNormalMagnitude` was needed (and still is, I believe) for grouped tables. It is problematic with plain tables though. It's magic out there. – Sulthan Oct 13 '17 at 19:39
1

Implement tableView(_:heightForHeaderInSection:) to return UITableViewAutomaticDimension.

This will suppress the section header in exactly the case where titleForHeaderInSection returns nil (and otherwise it will use the default header height from the table).

matt
  • 515,959
  • 87
  • 875
  • 1,141
0

If you explicitly tell iOS 11 to use a height of 0 in heightForHeaderInSection it will hide the section header. You can still use automatic header sizing by returning UITableViewAutomaticDimension for non-zero height headers. Here's an example of a solution to workaround the iOS 11 bug:

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
  guard shouldShowSectionHeader(section: section) else { return 0 }
  return UITableViewAutomaticDimension
}

You'll need to implement shouldShowSectionHeader to determine whether or not to show the section header.

ToddH
  • 2,801
  • 2
  • 28
  • 28
0

iOS 11.3, works in production

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

    return sections[section].headerViewModel?.makeView(bindImmediately: true)
}

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {

    return sections[section].headerViewModel == nil ? 0 : UITableViewAutomaticDimension
}
Mike Glukhov
  • 1,758
  • 19
  • 18
0

return empty view from viewForHeaderInSection

0

swift 3, 4 and 4.2

To hide your tableView header

tableView.tableHeaderView?.frame = CGRect.zero

and to show it back

tableView.tableHeaderView?.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: 66)
Akbar Khan
  • 2,215
  • 19
  • 27
0

What worked for me:

func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 0.01
}
Kaye
  • 153
  • 2
  • 12