Search code examples
iosuitableviewios8autolayout

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


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?


Solution

  • 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
    }