1

I'm working on a company's project and I have this problem when testing my table view on iOS 11 GM. It did work well on iOS 10. It is simple, I have three sections with header. When I tap on section header, my section will collapse/extend. Here's how I do it:

ViewController:

class ViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!

var sectionsOpened: [String: Bool] = [
    "section1": true,
    "section2": true,
    "section3": true
]

func isSectionOpened(section: String) -> Bool {
    return sectionsOpened[section]!
}

@objc func toggleSection1() {
    sectionsOpened["section1"] = !sectionsOpened["section1"]!
    toggle(sectionIndex: 0)
}

@objc func toggleSection2() {
    sectionsOpened["section2"] = !sectionsOpened["section2"]!
    toggle(sectionIndex: 1)
}

@objc func toggleSection3() {
    sectionsOpened["section3"] = !sectionsOpened["section3"]!
    toggle(sectionIndex: 2)
}

func toggle(sectionIndex: Int) {

    self.tableView.reloadSections([sectionIndex], with: .automatic)
    self.tableView.scrollToRow(at: IndexPath(row: 0, section: sectionIndex), at: .top, animated: true)
}

Table view dataSource:

extension ViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
    return 3
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 1
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "testCell", for: indexPath)
    let label = cell.viewWithTag(1) as! UILabel
    label.text = "TEST \(indexPath.section) - \(indexPath.row)"
    return cell
}

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let headerView = UILabel(frame: CGRect(x: 0, y: 0, width: 320, height: 60))
    headerView.backgroundColor = UIColor.green
    headerView.text = "Header \(section)"
    var gesture: UITapGestureRecognizer?
    if section == 0 {
        gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection1))
    } else if section == 1 {
        gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection2))
    } else if section == 2 {
        gesture = UITapGestureRecognizer(target: self, action: #selector(toggleSection3))
    }
    headerView.addGestureRecognizer(gesture!)
    headerView.isUserInteractionEnabled = true
    return headerView
}

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

func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
    return CGFloat.leastNormalMagnitude
}

Table view delegate:

extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if !isSectionOpened(section: "section\(indexPath.section+1)") {
        return 1
    }
    if indexPath.section == 0 {
        return 350
    } else if indexPath.section == 1 {
        return 400
    } else if indexPath.section == 2 {
        return 350
    }
    return 500
}

First notice is that scrollToRow behave weird, it go to top, then scroll down to the position. Then, after trying to open/close the 3 headers, scrolling up/down, sometimes I got this duplicate header problem:

enter image description here
Duplicate header when reloading sections (photo) '

Thank you in advance for your help. I really need to make this to work because iOS 11 will come next Tuesday...

shishir
  • 851
  • 3
  • 11
  • 27
qtngo
  • 1,594
  • 1
  • 11
  • 13
  • Ever found a solution for this? tableView.estimatedRowHeight = 0 works. However, I wouldn't want to calculate all row heights manually. – Matan Guttman Oct 17 '17 at 14:46

2 Answers2

3

I had the same problem and fixed it by using:

self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0;
Supala
  • 320
  • 2
  • 9
  • If we set estimatedRowHeight to zero, then what about self sizing cell height with constraints??? – Sabby Oct 06 '17 at 07:25
  • I don't know since I do not use constraints – Supala Oct 09 '17 at 11:58
  • Cool.. looks it can not be used with self sizing cells.. so we have to manually calculate the height.. – Sabby Oct 10 '17 at 07:29
  • 1
    Doesn't work for me, only making disappear header (height will be 0). But when i set a constraint on my header, duplicate header are always here. IOS 11 is the worst version ever made... – E. Spiroux Oct 24 '17 at 13:05
2

The issue has to do with the estimated row height when using self sizing cells. It seems if we do not provide very accurate estimations a couple of issues may appear across different version of iOS when reloading table view rows. On iOS 9 and 10 the table view may jump towards the top. On iOS 11 it seems section headers may duplicate if they are sticky at the top when reloading a row.

One solution is to cache the hight of the row as it is displayed and then provide this height back to the tableview as the estimated height, when required. Annoying to have to resort to something like this, but it solves both issues for me across iOS 9, 10 and 11.

See also this issue

Darumar
  • 71
  • 9