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

outerstorm
  • 712
  • 3
  • 7
  • 16

6 Answers6

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
PGDev
  • 23,751
  • 6
  • 34
  • 88
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;
}
Jaffer Sheriff
  • 1,444
  • 13
  • 33
  • I didn't see any change in behavior with or without this. – outerstorm May 30 '17 at 12:56
  • It seems working fine without any issues for me in 10.3.2 . What is your ios version ? – Jaffer Sheriff May 30 '17 at 12:59
  • 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. – outerstorm May 30 '17 at 17:47
  • I was calling `[table setEsitimatedHeaderHeight]` but I had to include this for it to work for me, don't understand why... but it worked – Shayno Dec 03 '17 at 03:37
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

weikel
  • 164
  • 4
  • 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. – Tim May 31 '17 at 20:00
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
}
Anuj Panwar
  • 683
  • 7
  • 10
  • While this likely would work, it kills virtualization. Which is like 50% of the reason to use tableviews! – Tim May 31 '17 at 20:01
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

Alex Chase
  • 960
  • 1
  • 7
  • 11
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

Kampai
  • 22,848
  • 21
  • 95
  • 95