iOS:在 willDisplayCell 中做动画时,UITableView 加速变慢

Posted

技术标签:

【中文标题】iOS:在 willDisplayCell 中做动画时,UITableView 加速变慢【英文标题】:iOS: UITableView acceleration slows down when doing animations in willDisplayCell 【发布时间】:2016-09-08 18:18:55 【问题描述】:

最新编辑: 经过更多实验后,我确信我遇到了触摸处理问题。 我创建了一个自定义单元格,其子视图填充了内容视图宽度的一半。我正在为该子视图而不是内容视图设置动画。

在触摸单元格右侧的同时滚动表格视图可以正常工作。 滚动单元格左侧(动画视图所在的位置)的表格视图会导致延迟。

编辑: 最后,我正在尝试制作类似to this one 的动画。 该问题中接受的答案与我要问的问题相同。 下面的代码是重现问题的最简单的代码。

dequeCellForIndexPath 而不是willDisplayCell 中制作动画没有区别。

willDisplayCell 中制作动画时,表格视图滚动动画会变慢。当您尝试在 tableview 中快速滚动时,滚动会失去加速度。

重现该问题的一些示例代码:

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate 

    @IBOutlet var tableView: UITableView!

    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 

        cell.contentView.layer.opacity = 0.0

        UIView.animateWithDuration(0.3) 
            cell.contentView.layer.opacity = 1.0
        
    

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = UITableViewCell()

        cell.textLabel?.text = "\(indexPath.row)"

        return cell
    


    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 
        return 300
    

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 5000
    

当单元格的高度很大时,问题会变得最严重。

有没有办法避免这种情况,或者有更好的功能来做单元格动画?

相关:Blogpost on doing animations like this

编辑 2: 说明问题的 GIF:

在所有 3 种情况下,我都尽可能快地在手机上滚动。

通过 willDisplayCell 中的动画,我到达第 60 行

没有动画,我到达第 400 行以上

willDisplayCell 中随机延迟 0 - 0.3 秒,阻塞主队列,我到达第 120 行

我猜想某处的触摸处理存在问题。

即使the library I use to visualize how fast I'm scrolling,在willDisplayCell 中使用动画时也会中断。

【问题讨论】:

"医生医生,我这样做会很痛!" “所以不要那样做!”说真的——在滚动时为已经发生的事情添加动画是一个可怕的想法。 @matt 很多应用程序都有用于单元格外观的自定义动画。 UITableView 动画单元格插入/删除。你能解释一下为什么动画出现在屏幕上的单元格是一个可怕的想法吗? 因为它压倒了渲染树和滚动卡顿——正如你自己所说的那样。 渲染不是这里的瓶颈。如果我在 willDisplayCell 方法中调用NSThread.sleepForTimeInterval(0.1),表格会严重滞后,但不会在滚动过程中失去加速度。 你不知道瓶颈是什么,除非你使用 Instruments 找出来。而且我没有看到任何证据表明你这样做了。 【参考方案1】:

尝试在您的cellForRowAtIndexPath 函数中使用tableView.dequeueReusableCellWithIdentifier(..)。我尝试了下面的代码,滚动性能对我来说看起来不错。我还添加了cell.contentView.layer.removeAllAnimations() 以在单元格消失后取消动画。

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
    guard let cell = tableView.dequeueReusableCellWithIdentifier(identifier) as? TableViewCell else  fatalError("where's my cell") 
    cell.textLabel?.text = "\(indexPath.row)"
    return cell


override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat 
    return 300


override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    return 5000


override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
    cell.contentView.layer.opacity = 0.0
    UIView.animateWithDuration(0.3) 
        cell.contentView.layer.opacity = 1.0
    


override func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) 
    cell.contentView.layer.removeAllAnimations()

【讨论】:

其实我明白你说的了,减速的时候还是有点波涛汹涌。不过最好还是出队而不是创建新单元格:) 感谢您的回复。是的,我知道这是最好的双端队列,而不是重新创建,但对于这个问题,它没有任何区别。就像 tableview 忽略了触摸或其他东西。您必须快速滚动才能注意到问题。您继续滚动,但表格视图不会像往常那样加速,而是平稳地减速。【参考方案2】:

因此,您当然应该将单元格出列而不是一直重新创建它们。在您的测试中,这不会有太大的不同,但随着您的细胞变得更加复杂,它会有所不同。

此外,您还需要考虑何时可以使用动画,以及动画应该有多大。集合视图上的库存动画仅用于添加的项目是有原因的 - 因为它是动画的有效使用。通常,例如,当用户向上滚动时,项目并不是新的,因此单元格滚动到视图中的任何动画都是一个令人困惑的范例。

现在,当向下滚动时,我可以看到您可能想要动画的原因。在这种情况下,您需要考虑滚动速度 - 如果您正在滚动,那么动画很难/无法看到,所以没有意义这样做...

所以,跟踪滚动速度。在简单的选项中,如果您的滚动速度超过某个阈值速度,则不要设置动画。在复杂选项中,根据滚动速度调整动画的大小,以便慢速滚动仍然占据大部分动画,而较快的滚动会调低动画,直到您达到该阈值并一起关闭动画。

【讨论】:

感谢您的回答。你能看看我的最新编辑吗?在达到一定阈值速度后禁用动画的好主意,我将使用它。 在我的真实项目中,我当然是对单元格进行出列。向下滚动时,每个单元格只发生一次动画。实际动画也比简单的淡入/淡出更复杂。我在这里给出的示例是重现我的问题的最简单的代码,所以我省略了很多细节。 我不知道,但我可以理解命中检测和动画视图是否存在问题,尽管与位置动画相比,不透明动画的问题较少。使用您的示例项目创建一个雷达并查看您获得的反馈可能是值得的。【参考方案3】:

正如其他人所说,首先您必须将一个单元格出列而不是每次都初始化一个新单元格。

如果你使用AutoLayout 来布局你的单元格,我建议删除它如果没有其他工作。 (这是一个很棒的功能,但它也需要大量的 cpu 时间)。

但我的解决方案是一种解决方法,不要使用UIViewAnimation。请改用UIScrollViewDelegate。在scrollViewDidScroll 方法中,取tableView 的可见单元格并通过直接计算它与屏幕底部的距离来设置最后一个单元格的alpha,无需任何动画。

【讨论】:

以上是关于iOS:在 willDisplayCell 中做动画时,UITableView 加速变慢的主要内容,如果未能解决你的问题,请参考以下文章

iOS - TableView willDisplayCell 动画仅在用户向下滚动而不是顶部时发生

UITableview 不通过 willdisplaycell 选择单元格

访问moreNavigationController的UITableView的-willDisplayCell方法

UITableView willDisplayCell 方法的不当行为

UITableView WillDisplayCell 方法在特定时间后调用?

UITableViewCell 中的 UILabel 将每个 willDisplayCell 的字体大小加倍