UITableView Section Header Automatic Height Not Updated Properly
Asked Answered
B

6

15

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

Bon answered 16/5, 2017 at 15:40 Comment(0)
D
3

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
Dirac answered 29/5, 2017 at 10:11 Comment(1)
I have tried this and haven't seen any change in behavior. The issue is still there in the sample app.Bon
H
3

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;
}
Heehaw answered 30/5, 2017 at 12:47 Comment(4)
I didn't see any change in behavior with or without this.Bon
It seems working fine without any issues for me in 10.3.2 . What is your ios version ?Heehaw
Also 10.3.2. I updated the question with a screenshot with the behavior from the sample app. Also, the sample app was updated on github with the suggested fixes.Bon
I was calling [table setEsitimatedHeaderHeight] but I had to include this for it to work for me, don't understand why... but it workedAstrionics
U
1

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

Undress answered 25/5, 2017 at 5:52 Comment(1)
That doesn't mesh with virtualization. The cells are de-queued and have to be resized every time that occurs. If not then virtualization is broken.Veedis
E
1

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
}
Enolaenormity answered 31/5, 2017 at 9:1 Comment(1)
While this likely would work, it kills virtualization. Which is like 50% of the reason to use tableviews!Veedis
M
1

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

Maladroit answered 31/10, 2018 at 22:43 Comment(0)
F
0

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

Fluidize answered 31/5, 2017 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.