UITableView Section Header Automatic Height Not Updated Properly

10.3k Views Asked by At

I am running into an issue with automatic/dynamic UITableView section header views that contain a UILabel that wraps (numberOfLines = 0). The height is not being calculated properly, especially once you scroll the table and the views are reused. Sometimes the UILabel wraps, sometimes it is truncated, sometimes one or more of the labels are not visible, and sometimes there is extra spacing between the labels. The custom view contains a vertical UIStackView with three UILabels, once of which wraps.

A complete sample app demonstrating the issue can be found at https://github.com/outerstorm/tableviewHeaderTest.

The section header heights are set to automatic in viewDidLoad with the following:

    tableView.sectionHeaderHeight = UITableViewAutomaticDimension
    tableView.estimatedSectionHeaderHeight = 30.0

and also have implemented the following heightForHeaderInSection just to try to get it to work:

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

I have also tried calling setNeedsLayout() and layoutIfNeeded() at various points to no avail. Any suggestions would be greatly appreciated.

Below is a screenshot of the behavior seen in the app. The first section is cutoff and the second section is too tall:

enter image description here

6

There are 6 best solutions below

1
On

In general

heightForHeaderInSection

always called before

viewForHeaderInSection

when using UITableViewAutomaticDimension, sectionheader height will only calculate once unless called tableview.reloadData() manually.

In the code, you change sectionheader text everytime. the height was calculate at the first time, and doesnt change automatic.

you can change setup func to:

func setup(someNum: Int) {

    let text = String(repeating: "This is where the really long text goes so that it will wrap lines appropriately", count: someNum)

    mainLabel.text = text
}

and pass the section index to the function

1
On

I have faced this kind of issue recently.

I solved it by setting preferredMaxLayoutWidth of multiline labels, i.e.

Before setting the value in labels, set their preferredMaxLayoutWidth as:

self.label1.preferredMaxLayoutWidth = self.label1.frame.size.width
self.label2.preferredMaxLayoutWidth = self.label2.frame.size.width
self.label3.preferredMaxLayoutWidth = self.label3.frame.size.width
4
On

Just add estimatedHeightForHeaderInSection function and return your estimated height. It will resolve your issue. Download your modified project from here

func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat 
{
    return 30;
}
0
On

Add

self.label1.preferredMaxLayoutWidth = self.label1.frame.size.width
self.label1.numberoflines = 0;
self.label1.linebreakmode = NSLineBreakByWordWrapping;

in awakeFromNib method or Kindly check this once

1
On

A workaround can be hold the header views in array and then return the height of view from estimated height method

for _ in 0 ..< data.count {

        let view = tableView.dequeueReusableHeaderFooterView(withIdentifier: "CustomHeaderView") as! CustomHeaderView
        view.setup()
        headerViews.append(view)
    }


func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat
{
    let view = headerViews[section] as? UIView
    return (view?.frame.size.height)!
}

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

    return headerViews[section] as? UIView
}
0
On

From the code's documentation:

// tableView:titleForHeaderInSection: or tableView:titleForFooterInSection: if the title is not nil.

In other words. UITableViewAutomaticDimension is only intended for use if you are providing a section title using titleForHeaderInSection or titleForFooterInSection