UITableViewCell与AutoLayout的性能不佳

时间:2013-06-05 20:29:37

标签: iphone ios performance uitableview autolayout

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

我已UITableView使用NSFetchedResultsController提供的数据源。在单独的视图控制器中,我使用[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,但没有任何改变。然后我将单元格的样式更改为Basic(或除Custom之外的任何内容),问题就消失了 - 即时添加新单元格。

我加倍检查,NSFetchedResultsControllerDelegate回调只被调用一次。如果我忽略它们并做[UITableView reloadSections:withRowAnimation:],没有任何改变 - 它仍然很慢。

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

以下是该问题的呼叫追踪: stack trace

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

更新1

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

尝试添加至少一组屏幕以感受性能问题。

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

5 个答案:

答案 0 :(得分:16)

自动布局确实会带来性能损失。但是,对于大多数情况来说,这并不是很明显。有一些边缘情况有复杂的布局,摆脱它会产生有意义的差异,但这不是真正的问题。

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

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

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

答案 1 :(得分:2)

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

以下是使其成为可能的代码:

- (void)viewDidLoad {
    [super viewDidLoad];

    ...        

    UINib *rowCellNib = [UINib nibWithNibName:@"RowCell" bundle:nil];
    [self.tableView registerNib:rowCellNib forCellReuseIdentifier:@"ROW_CELL"];
}

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

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

答案 2 :(得分:2)

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

[cell setNeedsUpdateConstraints];
[cell setNeedsLayout];

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

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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

- (NSString*) reuseIdentifier {

    return self.dynamicReusableIdentifier;
}

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

返回的正确可重用标识符

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

更新了重复使用的单元格的静态子视图(标签,图像),没有任何自动布局更改。