【问题标题】:UITableViewCell bad performance with AutoLayoutUITableViewCell 使用 AutoLayout 时性能不佳
【发布时间】:2013-06-01 16:52:53
【问题描述】:

我有点坚持这个......非常感谢任何帮助。我已经花了很多时间调试这个。

我有UITableViewNSFetchedResultsController 提供的数据源。在一个单独的视图控制器中,我使用[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:] 将新记录插入CoreData,保存托管对象上下文并关闭该控制器。非常标准的东西。

然后由NSFetchedResultsController 接收托管对象上下文中的更改:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
        [self.tableView beginUpdates];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
        [self.tableView endUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    switch (type) {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationNone];

            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

            break;

        case NSFetchedResultsChangeUpdate:
            [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];

            break;

        case NSFetchedResultsChangeMove:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
            [self.tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationNone];

            break;
    }
}

这就是问题出现的地方——它需要很长时间(在 iPhone 4 上大约 3-4 秒)才能做到这一点。似乎时间都花在了计算单元格的布局上。

我已经从单元格中删除了所有内容(包括自定义子类),只留下了UILabel,但没有任何改变。然后我将单元格的样式更改为基本(或除自定义之外的任何内容)并且问题消失了 - 立即添加了新单元格。

我已经检查了两倍,NSFetchedResultsControllerDelegate 回调只被调用一次。如果我忽略它们并执行[UITableView reloadSections:withRowAnimation:],则没有任何变化——它仍然很慢。

在我看来,默认单元格样式禁用了自动布局,这使得它们非常快。但如果是这样的话——为什么当我推送UITableViewController 时一切都加载得很快?

这是该问题的调用跟踪:

所以问题是——这里发生了什么?为什么细胞渲染这么慢?

更新 1

我构建了一个非常简单的演示应用程序来说明我遇到的问题。这是来源 - https://github.com/antstorm/UITableViewCellPerformanceProblem

尝试添加至少一屏单元格来感受性能问题。

另请注意,直接添加一行(“立即插入!”按钮)不会导致任何缓慢。

【问题讨论】:

  • 只是为了确认 - 您正在为您的自定义单元格分配一个唯一标识符并将它们出列而不是重新创建,对吗?
  • 以及在单元格上指定了哪些自动布局规则?
  • 是的,我正在分配一个唯一标识符并使​​用[UITableView dequeueReusableCellWithIdentifier:forIndexPath:] 将单元格出列。
  • 关于布局规则,我尝试了不同的——固定宽度/高度和居中的UILabel,以及固定的顶部/左侧边距,结果是一样的。
  • 我遇到了完全相同的性能问题。我仍在调查这个问题,但同时我刚刚放弃了原型单元,而是使用自定义子类。以编程方式在initWithStyle 中设置我的单元格标签和图像的框架可以避免自动布局性能问题,但当然你也会失去自动布局带来的所有好处。有 some good 阅读了自动布局性能,这可能会对您所看到的内容有所帮助。

标签: iphone ios performance uitableview autolayout


【解决方案1】:

我在 iOS 7 的 [table reloadData] 中也有同样的表现。 就我而言,我解决了这个问题,将单元格配置代码从[cell layoutIfNeeded] 替换为以下代码:

[cell setNeedsUpdateConstraints];
[cell setNeedsLayout];

然后返回单元格。一切似乎都很好。我希望这可以帮助其他遇到同样问题的人。

【讨论】:

  • 也为我工作。简单!谢谢,
【解决方案2】:

在 uitableviewcell 子类中使用复杂的自动布局时,我遇到了类似的问题。我的单元格布局取决于来自服务器的数据,但单元格状态的数量限制为六个。因此,每次我从 dequeueReusableCellWithIdentifier 收到 nil (创建一个新单元格)时,我都会为刚刚创建的单元格(self.dynamicReusableIdentifier)设置一个动态/自定义重用标识符。在 UITableViewCell 子类(ACellSubclass)中重载了reuseIdentifier getter:

- (NSString*) reuseIdentifier {

    return self.dynamicReusableIdentifier;
}

self.dynamicReusableIdentifier 字符串的生成基于单元类(ACellSubclass)中定义的静态方法(configureDynamicReuseIdentifier)。 tableView:cellForRowAtIndexPath 然后选择从

返回的正确的可重用标识符

cell = [tableView dequeueReusableCellWithIdentifier:[ACellSubclass configureDynamicReuseIdentifier:someData]];

并且重用单元格的静态子视图(标签、图像)在没有任何自动布局更改的情况下更新。

【讨论】:

    【解决方案3】:

    如果您仍想在表格视图单元格中使用自动布局,有一些方法可以提高性能。这是一个多步骤的过程,但是一旦您这样做了,除了单元格中其他所有内容的布局之外,您还可以让自动布局引擎确定单元格的垂直大小。

    您可以在此处找到更多详细信息:Using Auto Layout in UITableView for dynamic cell layouts & variable row heights,其中包括一些使用 iOS 8 时可以使用的快捷方式。

    【讨论】:

      【解决方案4】:

      好的,我终于在不牺牲动画的情况下解决了这个问题。我的解决方案是在禁用 AutoLayout 的单独 Nib 文件中实现 UITableViewCell 的接口。加载时间稍长,需要自己定位子视图。

      这是实现它的代码:

      - (void)viewDidLoad {
          [super viewDidLoad];
      
          ...        
      
          UINib *rowCellNib = [UINib nibWithNibName:@"RowCell" bundle:nil];
          [self.tableView registerNib:rowCellNib forCellReuseIdentifier:@"ROW_CELL"];
      }
      

      当然,您需要一个包含单元格视图的 RowCell.nib 文件。

      虽然原始问题没有解决方案(这对我来说显然是一个错误),但我正在使用这个。

      【讨论】:

      • 我不明白这个解决方案。是否禁用了自动布局,或者您是否以某种方式欺骗系统以获得性能?
      • 诀窍是为每个单元格设置单独的 Xib 文件(禁用自动布局)。这样整个界面都启用了自动布局,但每个单元格都没有。
      • 但不是每次都从表格的笔尖加载单元格。表视图从内存中取出可重用单元格,并且它们已经从 nib 加载。所以基本上你已经禁用了单元格的自动布局。这是如何解决问题的?
      • 我建议你尝试运行我在 git 上共享的项目。但似乎问题在于约束性能,在重新加载后重新计算整个单元格集。不是因为笔尖负载。
      • 我已经尝试过您的项目,并且似乎在 iPhone 4 上的 iOS 7.1 和 iOS 7.1.1 上运行良好,也许这是 iOS 6 或 iOS 7.0 的问题?顺便说一句,如果您已经调用了 didChangeObject 方法并在其中处理 tableview 更改,我认为没有必要调用 [self.tableView beginUpdates] 和 [self.tableView endUpdates]。
      【解决方案5】:

      自动布局确实会影响性能。但是,在大多数情况下,它并不明显。在某些具有复杂布局的边缘情况下,摆脱它会产生有意义的变化,但这并不是真正的问题。

      我没有很好的解释为什么应用程序的行为方式是这样,但至少我有一个解决方案:如果表格视图不在屏幕上,不要进行表格视图更新。这会导致这种奇怪的行为。

      为此,您可以例如在获取的结果控制器的委托方法中检查self.tableview.window != nil。然后,您只需将[self.tableview reloadData] 添加到viewWillAppear,以便表格视图在出现在屏幕上之前更新其数据。

      希望对您有所帮助。如果有人对这种奇怪的行为有很好的解释,请告诉我:)

      【讨论】:

      • 是的,[UITableView reloadData] 确实解决了这个问题,但是你不会得到动画,它需要重新配置屏幕上的所有单元格。使用[UITableView reloadSections:withRowAnimation:]可以用动画重新加载表格数据,但是和原来的方法一样慢。
      • 这很有效,谢谢。我为此问题打开了 rdar://15175803。
      • 这也解决了我的类似问题。我在viewWillAppear 中保存了一个托管对象上下文。转到viewDidAppear 解决了性能问题:如果视图不在屏幕上,布局会变得更慢。
      • 感谢您的提示!当表格不在屏幕时避免使用beginUpdates/endUpdates 并调用reloadData 也解决了我的性能问题。从堆栈跟踪中我可以看出,这种行为是由 UITableView 在调用 endUpdates 时强制布局引起的。当它在屏幕外 (tableView.window == nil) 时,触发 Auto Layout 会导致它为每个布局操作创建一个屏幕外布局引擎,这很慢。但是在屏幕上时,会使用窗口的布局引擎,所以当使用reloadData 时,一旦表格回到屏幕上,布局就会正常执行。
      • 当视图不在屏幕上时避免更新真的很有帮助,谢谢!但是,由于(理论上?)beginUpdates/endUpdates 调用不平衡的风险,我对在委托方法中检查 self.tableview.window != nil 犹豫不决。相反,我将fetchedResultsController 的代表设置为viewWillDisappear 中的nil,并将其重新连接到viewDidAppear 中的self(当然还有reloadData)。这应该确保当视图离开屏幕时不会触发 fetchedResultsController 委托方法。到目前为止运作良好。手指交叉。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-17
      • 2013-06-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多