UITableView 动态单元格高度在过渡或旋转时中断

Posted

技术标签:

【中文标题】UITableView 动态单元格高度在过渡或旋转时中断【英文标题】:UITableView dynamic cell height breaks on transition or rotation 【发布时间】:2014-11-06 07:40:34 【问题描述】:

目前我正在处理快速和动态的表格单元格高度。我在xcode6.1上为ios8.1开发了一个简单的app:https://github.com/ArtworkAD/DynamicCellTest

因此,为了实现与单元格内容一致的单元格高度,我执行以下操作:

在情节提要中将标签行设置为 0 将标签字体设置为系统 为单元格中的标签设置约束 添加self.tableView.rowHeight = UITableViewAutomaticDimension 不要覆盖heightForRowAtIndex方法

需要最少的代码:

class MyTableViewController: UITableViewController 

    var entries:Array<String> = [String]()

    override func viewDidLoad() 
        super.viewDidLoad()

        //create dummy content
        var i = 0
        while i < 10 
            entries.append("\(i) Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor")
            entries.append("\(i+1) Lorem ipsum dolor sit amet")
            i = i + 2;
        

        self.tableView.rowHeight = UITableViewAutomaticDimension

    

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)  -> Int 
        return self.entries.count
    

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 

        let cell = self.tableView.dequeueReusableCellWithIdentifier("basic_cell", forIndexPath: indexPath) as UITableViewCell

        var label = cell.viewWithTag(13)

        if let unwrappedLabel = label as? UILabel 
            unwrappedLabel.text = self.entries[indexPath.row]
        

        return cell
    

左图显示了上述代码的结果。单元格高度随着标签的内容而增长,一切都很好。但是,当您单击显示指示器到详细视图并再次返回时,您会得到正确的图像。为什么会这样??

这个问题的一个不好的解决方案是重写这个方法:

override func viewWillDisappear(animated: Bool) 
    super.viewWillDisappear(animated)
    self.tableView.reloadData()


override func viewDidAppear(animated: Bool) 
    super.viewDidAppear(animated)
    self.tableView.reloadData()


override func viewDidDisappear(animated: Bool) 
    super.viewDidDisappear(animated)
    self.tableView.reloadData()

这样就解决了上面的问题,但是这个解决方法好像不对吧?它的一个副作用是,当self.tableView.reloadData() 被调用时,表格视图端口会跳转到第一个看起来不太好的单元格。

有人知道我做错了什么吗?随意克隆我的 repo https://github.com/ArtworkAD/DynamicCellTest 并进行测试。

【问题讨论】:

我认为willTransitionToTraitCollection:withTransitionCoordinator: 是重新加载表格视图数据的更好地方。 @VitoZiv 问题依然存在。 这似乎是个问题。找到这个openradar.appspot.com/17799811 您是否检查过是否真的加载了您布局的 UITableViewCell 的实例?可能是数据源只是分配了一个标准 UITableViewCell 因此 numberOfLines 设置为 1 和 truncateTail。 它通过在返回单元格之前添加 cell.setNeedsDisplay() 和 cell.setNeedsLayout() 来工作。 【参考方案1】:

添加这个似乎可以解决旋转问题。

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) 
    self.tableView.estimatedRowHeight = 36.5

但是,我有另一种情况,单元格中有 4 个标签,也有相同的旋转问题,添加这还不够,我最终用重新加载可见单元格替换了 self.tableView.estimatedRowHeight = 36.5

【讨论】:

解决方案不是很理想,希望有人能提出一个更优雅的方案。 这里有同样的问题,但没有用。您能解释一下如何重新加载可见单元格吗?谢谢,西德。 @sidd,[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows] withRowAnimation:UITableViewRowAnimationNone]; 唯一对我有用的是重新加载可见单元格。 这行得通。这么多小时,我所要做的就是这样吗? RIP【参考方案2】:

我刚刚通过在UITableViewDelegate 对象中覆盖tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) 解决了这个问题。

func tableView(tableView: UITableView, 
               estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) 
               -> CGFloat 
    return 200

更改确切的返回值显然没有效果。细胞总是正确地自我调整大小。我不知道为什么提供估计的高度会导致自动布局启动,但确实如此。 iOS 8.1.2,Xcode 6.1。

【讨论】:

【参考方案3】:

该链接很好地解释了需要做什么。

Using Auto Layout in UITableView for dynamic cell layouts & variable row heights

对于您的具体问题,您缺少两行代码。

viewDidLoad 下你的tableView.rowHeight 添加self.tableView.estimatedRowHeight = 64

上面将允许 tableView 为屏幕外单元格分配估计的高度值以提高性能,它还计算滚动条高度。

主要问题是您在cellForRowAtIndexPath 中缺少unwrappedLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds),这会告诉标签其首选的最大宽度,以便它可以计算多行文本所需的高度。通常你会在表格视图单元子类中这样做。

我已经在您的项目中对此进行了测试,并且可以正常工作

【讨论】:

我花了 3 天时间阅读这些文章 :) 如果你有时间,我建议你查看我的代码。 抱歉,我现在不能这样做,因为我正在参加培训课程。我以前做过这个,所以我有一个解决方案,一旦我回到笔记本电脑上,我就会进行编码。 :) estimatedRowHeight 设置为任何值修复了单元格高度在推送时被破坏的问题 - 仍然存在一个问题:推送时,tableview 的垂直位置被改变,并且选定的单元格跳出视图。【参考方案4】:

你必须设置estimatedRowHeight:

// setup automatic row height
self.tableView.rowHeight = UITableViewAutomaticDimension

// whatever you think is the approximate row height
self.tableView.estimatedRowHeight = 44

【讨论】:

【参考方案5】:

这里还遇到了一个替代方案http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html。评论提到 -

在从 cellForRowAtIndexPath 返回之前,只需将单元格布局设置为子视图:

[cell setNeedsDisplay];
[cell layoutIfNeeded];

【讨论】:

这不起作用。你可以用我的项目测试一下。 让我克隆你的项目并尝试 您可以将您的更改推送到 github 存储库吗?我添加了这两行,但它对我没有任何改变。 我的意思是真正的问题是当您单击一个单元格并返回时。你仍然得到正确的结果吗? 现在我意识到当你从细节视图控制器点击返回按钮时它会恢复。

以上是关于UITableView 动态单元格高度在过渡或旋转时中断的主要内容,如果未能解决你的问题,请参考以下文章

旋转时表格单元内标签的动态高度

UITableView 自定义单元格高度未正确管理 swift 中的动态约束

UITableview 高度根据单元格设置

UITableView - 由单元格本身控制的动态单元格高度

如何让 UITableView 在动态高度单元格中滚动到底部?

UITableView:单击按钮时如何动态更改单元格高度?