我有点坚持这个...任何帮助都非常感激。我已经花了很多时间来调试它。
我已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
时,为什么一切都会很快加载?
以下是该问题的呼叫追踪:
所以问题是 - 这里发生了什么?为什么细胞渲染得这么慢?
更新1
我已经构建了一个非常简单的演示应用程序来说明我遇到的问题。这是源 - https://github.com/antstorm/UITableViewCellPerformanceProblem
尝试添加至少一组屏幕以感受性能问题。
另请注意,直接添加行(“立即插入!”按钮)不会导致任何缓慢。
答案 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]];
更新了重复使用的单元格的静态子视图(标签,图像),没有任何自动布局更改。