UITableViewCell 布局在重用单元格之前不会更新

Posted

技术标签:

【中文标题】UITableViewCell 布局在重用单元格之前不会更新【英文标题】:UITableViewCell layout not updating until cell is reused 【发布时间】:2016-11-10 18:05:54 【问题描述】:

我有一个 UITableView,我用自动调整单元格填充。 UITableView 设置相当简单:

    tableView.estimatedRowHeight = 70
    tableView.rowHeight = UITableViewAutomaticDimension

就像苹果在这里推荐的那样:https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html

要启用自调整大小的表格视图单元格,您必须设置表格视图的 UITableViewAutomaticDimension 的 rowHeight 属性。你还必须 为estimatedRowHeight 属性赋值。只要两者 设置好这些属性后,系统使用 Auto Layout 来计算 行的实际高度。

在配置单元格时,我还会禁用/启用一些约束以实现所需的外观。这就是事情变得有趣的地方。在重新使用单元格之前,不会更新单元格布局。字面上地。您可以调用layoutIfNeeded()setNeedsLayout()layoutSubviews() 或任何其他方法,您无法强制单元格更新其布局。

所有其他方面都工作得很好:标签确实改变了它们的文本,你隐藏/取消隐藏视图,但布局被卡住,直到单元格被重用。

问题:是什么原因造成的以及如何避免这种行为?

【问题讨论】:

【参考方案1】:

不幸的是,提供的答案/cmets 都没有为我解决。我总是以最初不正确的布局结束。只有在重用单元格或调用reloadData()(在表格视图上)后,它才能正确显示。

以下是唯一对我有用的东西。我不是这种 hack 的忠实粉丝,但是在这个看似非常简单的布局问题上花了大约半天时间后,我就放弃了。 >.

override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    DispatchQueue.main.async 
        self.tableView.reloadData()
    

或者,您也可以在viewDidAppear 中调用reloadData(没有DispatchQueue hack),但是当布局从“不正确”跳转到“正确”时,您可以清楚地看到“跳转”。

无论如何,只是想分享我的经验,希望这对其他人有所帮助。干杯!

【讨论】:

viewWillAppear 总是在主线程上调用。 这不是关于线程,而是关于异步“延迟”。 在搜索了 SO、Apple 论坛和互联网上的随机位置之后,这是唯一真正为我解决的问题。【参考方案2】:

我也遇到了你的问题。而不是删除

tableView.estimatedRowHeight = 70

我刚刚在 cellForRow 方法的末尾添加了一个 layoutIfNeeded,就在返回单元格本身之前:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as? MyCustomCell
    ...
    cell?.layoutIfNeeded()
    return cell!

结果:单元格布局总是完美的,第一次和每次重复使用。

【讨论】:

【参考方案3】:

就我而言,问题是由estimatedRowHeight 引起的。

只需删除此行

tableView.estimatedRowHeight = 70

解决了我的问题。 Cell 正确更新了它的布局,它几乎解决了我的问题。

但是,您很可能会遇到另一个麻烦,因为您的单元格的高度设置为 43.5 磅。您的日志也将充满自动布局错误,其中将包括这样的一行

<NSLayoutConstraint:0x600000097570 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7fd4ee511d20.height == 43.5   (active)>

显然,如果您不提供estimatedRowHeight,表格视图会对单元格的内容视图设置 43.5 磅的高度限制,并且如果您的单元格的“内部”高度不匹配(概率为 99.99%),然后它会将错误放入日志中。

如何避免该错误?我还不知道。 我发布了一个关于这个问题的问题,一旦我找到答案,我会在这个问题中提供一个链接。

【讨论】:

【参考方案4】:

在重用单元格之前不会更新单元格布局

如果您希望 tableview 反映更改的单元格布局。

改变单元格布局后重绘表格视图

        tableView.beginUpdates()
        tableView.setNeedsDisplay()
        tableView.endUpdates()

例如:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        guard let cell = tableView.cellForRow(at: indexPath) as? CustomCell else  return 
        cell.collapseDescriptionLabel()
        
        // redraw the tableView
        tableView.beginUpdates()
        tableView.setNeedsDisplay()
        tableView.endUpdates()
    

【讨论】:

【参考方案5】:

您不需要使用layoutIfNeeded()setNeedsLayout()layoutSubviews() 来强制布局。您可以使用tableView.beginUpdates()tableView.endUpdates()

例如:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        let cell = tableView.cellForRow(at: indexPath)
        tableView.beginUpdates()
        cell.heightConstraint.constant = 50
        tableView.endUpdates()

【讨论】:

以上是关于UITableViewCell 布局在重用单元格之前不会更新的主要内容,如果未能解决你的问题,请参考以下文章

TouchesBegan 没有在 UIView 中作为 UITableViewCell 的子视图触发

我的 UITableViewCell 正在重用以前的单元格 UIImage

关于单元重用的自定义 UITableViewCell 约束问题

UItableviewCell 单元格重用问题

无法从出列可重用 UITableViewCell 加载自定义单元格

UITableViewCell 重用和图像重新渲染