Is it possible to obtain a dynamic table view section header height using Auto Layout?

IosUitableviewIos8Autolayout

Ios Problem Overview


New in iOS 8, you can obtain 100% dynamic table view cells by simply setting the estimated row height, then layout your elements in the cell using Auto Layout. If the content increases in height, the cell will also increase in height. This is extremely useful, and am wondering if the same feat can be accomplished for section headers in a table view?

Can one, for example, create a UIView in tableView:viewForHeaderInSection:, add a UILabel subview, specify auto layout constraints for the label against the view, and have the view increase in height to fit the label's contents, without having to implement tableView:heightForHeaderInSection:?

The documentation for viewForHeaderInSection states: "This method only works correctly when tableView:heightForHeaderInSection: is also implemented." I haven't heard if anything has changed for iOS 8.

If one cannot do that, what is the best way to mimic this behavior?

Ios Solutions


Solution 1 - Ios

This is possible. It is new right alongside the dynamic cell heights introduced in iOS 8.

To do this, use automatic dimension for the section header height, and if desired you can provide an estimated section header height. This can be done in Interface Builder when the table view is selected or programmatically:

Table view configuration in storyboard

tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 38

//You can use tableView(_:heightForHeaderInSection:) and tableView(_:estimatedHeightForHeaderInSection:)
//if you need to support different types of headers per section

Then implement tableView(_:viewForHeaderInSection:) and use Auto Layout to constrain views as desired. Be sure to fully constrain to UITableViewHeaderFooterView's contentView, especially top-to-bottom so the height can be determined by the constraints. That's it!

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {    
    let headerView = UITableViewHeaderFooterView()
    headerView.translatesAutoresizingMaskIntoConstraints = false
    headerView.backgroundView = {
        let view = UIView()
        view.backgroundColor = myCustomColor
        return view
    }()

    let headerLabel = UILabel()
    headerLabel.translatesAutoresizingMaskIntoConstraints = false
    headerLabel.text = "Hello World"
    headerView.contentView.addSubview(headerLabel)
    
    NSLayoutConstraint.activate([
        headerLabel.leadingAnchor.constraint(equalTo: headerView.contentView.leadingAnchor, constant: 16),
        headerLabel.trailingAnchor.constraint(equalTo: headerView.contentView.trailingAnchor, constant: -16),
        headerLabel.topAnchor.constraint(equalTo: headerView.contentView.topAnchor, constant: 12),
        headerLabel.bottomAnchor.constraint(equalTo: headerView.contentView.bottomAnchor, constant: -12)
    ])
    
    return headerView
}

Solution 2 - Ios

This can be accomplished by setting (or returning) the estimatedSectionHeaderHeight on your table view.

If your section header is overlapping your cells after setting estimatedSectionHeaderHeight, make sure that you're using an estimatedRowHeight as well.

(I'm adding this answer because the second paragraph contains an answer to an issue that can be found after reading through all of the comments which some might miss.)

Solution 3 - Ios

Got stuck in the same issue where header was getting zero height untill and unless I provide a fixed height in the delegate for heighForHeaderInSection.

Tried a lot of solutions which includes

self.tableView.sectionHeaderHeight = UITableView.automaticDimension
self.tableView.estimatedSectionHeaderHeight = 73

But nothing worked. My cell were using proper autolayouts too. Rows were changing their height dynamically by using the following code but section header weren't.

self.tableView.estimatedRowHeight = 135
self.tableView.rowHeight = UITableView.automaticDimension

The fix is extremely simple and weird too but I had to implement the delegate methods instead of 1 line code for the estimatedSectionHeaderHeight and sectionHeaderHeight which goes as follows for my case.

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

func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
    return 73
}

Solution 4 - Ios

Swift 4+

working(Tested 100%)

If you need both section as well row with dynamic height based on content then you can use below code:

On viewDidLoad() write this lines:

    self.globalTableView.estimatedRowHeight = 20
    self.globalTableView.rowHeight = UITableView.automaticDimension
    
    self.globalTableView.sectionHeaderHeight =  UITableView.automaticDimension
    self.globalTableView.estimatedSectionHeaderHeight = 25;

Now we have set row height and section height by using UITableView Delegate methods:

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
   {
       return UITableView.automaticDimension
   }
   func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
       
       return UITableView.automaticDimension
       
   }

Solution 5 - Ios

I tried

self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension;
self.tableView.estimatedSectionHeaderHeight = 25;

but it didn't size correctly header with multiline label. Added this to solve my problem:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    
    // Recalculates height
    tableView.beginUpdates()
    tableView.endUpdates()
}

Solution 6 - Ios

In my case:

  1. set programmatically not work.

self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension.

  1. set in storyboard not work.

  2. override heightForHeaderInSection worked.

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

test enviroment:

  • Mac OS 10.13.4
  • XCode Version 9.4.1
  • Simulator iPhone 8 Plus

Solution 7 - Ios

Yes, it works for me. I have to make more changes as below:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let label = UILabel()
    
    label.numberOfLines = 0
    label.text          = my own text
    
    return label
}

Solution 8 - Ios

Swift 5, iOS 12+

The only way it worked for me was

tableView.sectionHeaderHeight = UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 40
tableView.estimatedRowHeight = 80

Then setting constraints for custom header view elements so that auto layout engine could determine their x,y position and row height. i.e

NSLayoutConstraint.activate([
    titleLabel.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
    titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor),
    titleLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -24)
])

Solution 9 - Ios

I modified iuriimoz answer. Just replaced viewWillAppear method:

tableView.sectionHeaderHeight = UITableViewAutomaticDimension
tableView.estimatedSectionHeaderHeight = 25


override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Recalculates height
    tableView.layoutIfNeeded() 
}

Also add the tableView.layoutIfNeeded() to

override func viewDidAppear(_ animated: Bool) {
    
    super.viewDidAppear(animated)
    tableView.layoutIfNeeded()
}

For iOS 10

tableView.beginUpdates()
tableView.endUpdates()

have "fade" animation effect on viewWillAppear for me

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJordan HView Question on Stackoverflow
Solution 1 - IosJordan HView Answer on Stackoverflow
Solution 2 - IosClay EllisView Answer on Stackoverflow
Solution 3 - IosAhsan EbrahimView Answer on Stackoverflow
Solution 4 - IosMr.Javed MultaniView Answer on Stackoverflow
Solution 5 - IosiuriimozView Answer on Stackoverflow
Solution 6 - IosCodusView Answer on Stackoverflow
Solution 7 - Iosarg_vnView Answer on Stackoverflow
Solution 8 - IosbelotserkovtsevView Answer on Stackoverflow
Solution 9 - IosIrinaView Answer on Stackoverflow