Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView Section Header Automatic Height Not Updated Properly

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

like image 943
outerstorm Avatar asked May 16 '17 15:05

outerstorm


4 Answers

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
like image 145
PGDev Avatar answered Nov 14 '22 20:11

PGDev


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;
}
like image 35
Jaffer Sheriff Avatar answered Nov 14 '22 20:11

Jaffer Sheriff


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

like image 1
weikel Avatar answered Nov 14 '22 22:11

weikel


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
}
like image 1
Anuj Panwar Avatar answered Nov 14 '22 21:11

Anuj Panwar