自定尺寸单元格高度变化导致跳跃动画

Posted

技术标签:

【中文标题】自定尺寸单元格高度变化导致跳跃动画【英文标题】:Self sizing cells height change causes jumping animation 【发布时间】:2019-08-18 09:07:56 【问题描述】:

我已经使用 Autolayout 设置了一个带有动态高度单元格的表格视图,即 UITableView.automaticDimension。这工作正常。现在我想要实现的是改变单元格的高度并为其设置动画。问题是当我更改单元格高度并为其设置动画时,动画会奇怪地跳跃。仅当我向下滚动一点然后展开/折叠单元格时才会发生跳转。 我有一个简单的表格视图单元格。它有一个标签和一个具有固定高度约束的空 UIView。当我想折叠/展开单元格时,我只需将该高度约束的常数更改为 0 或 300。

我在网上尝试了许多可折叠的 tableview 示例。他们都有这个问题。 https://github.com/Ramotion/folding-cell 是一个例外,但它使用固定高度的单元格。

我已经尝试了很多选项来为单元格高度变化设置动画。

1-> 在 didSelectRow 上,我更改了高度约束并调用 tableview beginUpdate 和 endUpdates。不能解决跳转问题。

2-> 更改我的模型并调用 tableView.reloadRows。不能解决跳转问题。

这是我的表格视图单元设置的屏幕截图。 https://drive.google.com/open?id=12nba6cwRszxRlaSA-IhrX3X_vLZ4AWxy

此问题的视频链接: https://drive.google.com/open?id=19Xmc0PMXT0EuHTJeeGHm4M5aPoChAtf3

    override func viewDidLoad() 
        super.viewDidLoad()

        tableView.dataSource = self
        tableView.delegate = self
        tableView.estimatedRowHeight = 50
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedSectionHeaderHeight = 0
        tableView.estimatedSectionFooterHeight = 0
    

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! OuterTableViewCell
        let height: CGFloat = isCellExpanded[indexPath.row] ? 300 : 0

        cell.labelText.text = "Cell Number: \(indexPath.row + 1)"
        cell.buttonExpansionToggle.setImage(UIImage(named: isCellExpanded[indexPath.row] ? "arrow-down" : "arrow-right"),
                                            for: .normal)
        cell.viewContainerHeight.constant = height
        return cell
    

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        isCellExpanded[indexPath.row] = !isCellExpanded[indexPath.row]
        tableView.reloadRows(at: [indexPath], with: .automatic)
    

didSelectRow 的另一种形式:

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 

        guard let cell = tableView.cellForRow(at: indexPath) as? OuterTableViewCell else  return 
        isCellExpanded[indexPath.row] = !isCellExpanded[indexPath.row]
        let height: CGFloat = self.isCellExpanded[indexPath.row] ? 300 : 0
        cell.viewContainerHeight.constant = height
        cell.layoutIfNeeded()
        UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut, animations: 


            tableView.beginUpdates()
            tableView.endUpdates()

            // fix https://github.com/Ramotion/folding-cell/issues/169
            if cell.frame.maxY > tableView.frame.maxY 
                tableView.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.bottom, animated: true)
            
        , completion: nil)
    

我也尝试在动画块之外调用 beginUpdates() 和 endUpdates(),但问题仍然存在。

我希望动画流畅。希望有人可以提供帮助。如果有人可以在 github 上设置一个简单的演示项目,那就太棒了。

演示项目链接:https://gitlab.com/FahadMasoodP/nested-tableviews

感谢任何形式的帮助。谢谢。

【问题讨论】:

你能把你的示例项目放上去吗? @ThanhVu 我现在已经包含了示例项目的 gitlab 链接 gitlab.com/FahadMasoodP/nested-tableviews 能否请您在表格委托方法中提供高度。而在 didSelect 方法中只是重新加载特定的行。看看进展如何? 根据我的经验,请不要使用估计的行高,而是使用高度方法。 @JayrajVala 我不能使用固定高度。使用 Autolayout 自动计算单元格大小,正如您在 viewDidLoad 中看到的那样,我已将行高设置为 UITableView.automaticDimension 【参考方案1】:

我的解决方案是添加动态估计行高。我认为这是 UIKit 的错误。不会出现 ios 13 问题。

首先,您需要添加属性estimateRowHeightStorage以通过索引路径存储估计高度

var estimateRowHeightStorage: [IndexPath:CGFloat] = [:]

其次,您需要存储当前单元格的高度以备后用

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    estimateRowHeightStorage[indexPath] = cell.frame.size.height

Final,您使用estimateRowHeightStorage 设置估计行高。

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat 
    if estimateRowHeightStorage[indexPath] != nil 
        return estimateRowHeightStorage[indexPath]!
    

    return 50

跑步和感受。

我确实在您的情况下找到了新的解决方案。如果在展开时硬固定高度。只需要改变一些东西。 将以上代码全部替换为estimatedHeightForRowAt函数

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat 
    let height: CGFloat = isCellExpanded[indexPath.row] ? 373 : 73

    return height

【讨论】:

感谢您的回答。我尝试了这个解决方案,它似乎适用于前几个单元格,但后来我发现问题仍然存在。我已经在 gitlab 上更新了这个项目,所以你可以检查一下。如果你打开最后 5-6 个单元格,然后从底部开始一个一个地关闭,你会发现问题仍然存在。 虽然单元格大小不会固定/预先计算,但我尝试了您更新的解决方案。不工作。这是你更新后的视频drive.google.com/open?id=1EsKr97ZTA1z2V_kmCBsr7bNi-V-cSlje 我在真机上试过了,问题依旧

以上是关于自定尺寸单元格高度变化导致跳跃动画的主要内容,如果未能解决你的问题,请参考以下文章

具有动态单元格高度的 UITableView 的 reloadData() 导致跳跃滚动

当上边距不完全可见时,UITableView 单元格展开动画会发生变化

在 willDisplay 中配置自定尺寸单元格

嵌入在 UICollectionViewCell 中的自定尺寸 UITableView

动画下拉UIView时TableView单元格不改变高度

动画 UICollectionView 单元格大小更改和重新定位周围的单元格