NSFetchedResultsControllerDelegate的文档提供以下示例代码
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
当我创建一个新的NSManagedObject时, NSFetchedResultsChangeInsert 会触发(太棒了!)。当我更改属性的值(用于单元格的标题)时,会触发 NSFetchedResultsChangeUpdate 。不幸的是,除非我重新加载表格,部分或行,否则新标题不会自动显示。实际上,如果新名称导致结果集的排序方式不同,那么 NSFetchedResultsChangeMove 会触发,因为提供的代码会重新加载整个部分,所以一切都很好。
UITableView有一个方法 reloadRowsAtIndexPaths:withRowAnimation 所以我尝试在 NSFetchedResultsChangeUpdate 代码块下使用它。它确实有效......但是这个特定方法的文档看起来好像我不需要它(注意最后一行):
重新加载一行导致 表视图询问其数据源 该行的新单元格。桌子 动画那个新细胞 激活旧排。叫这个 方法,如果您想提醒用户 细胞的价值在变化。 但是,如果没有通知用户 重要 - 也就是说,你只是想 更改单元格的值 显示 - 你可以获得一个单元格 特定行并设置其新值。
是的,如果我记录发生了什么,何时
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
在 NSFetchedResultsChangeUpdate 上调用,它能够检索最新的“名称”值并将其设置在单元格的textLabel中。除非重新加载,否则名称不会在单元格中呈现。即使我只是单击名称显示的单元格。请注意,要重新创建此行为,您必须创建一个新的托管对象,然后为其指定一个名称,使其在NSFetchedResultsController中对FIRST进行排序。这样,NSFetchedResultsChangeMove就不会触发(因为它重新加载了该部分,所以它可以正常工作)。
我错过了什么或者这是预期的行为吗? reloadRowsAtIndexPaths 的“讨论”让我相信我应该能够简单地设置单元格的textLabel,而无需重新加载行,部分或表格。
答案 0 :(得分:3)
您应该调用[cell setNeedsLayout]
或/和[cell setNeedsDisplay]
以使细胞刷新,具体取决于您的细胞实施情况。
如果您像往常一样编写子视图的单元格,则依赖于- layoutSubviews
,因此您应该调用[cell setNeedsLayout]
。
如果您使用– drawRect:
直接绘制单元格,则应调用[cell setNeedsDisplay]
。
如果同时使用构图和绘图,则应同时调用它们。
答案 1 :(得分:1)
虽然您不需要重新加载单元格以进行更改,但您必须记住,iPhone会尽可能地缓存视图。一旦新配置了单元格,就需要在单元格上调用setNeedsDisplay以触发重绘。
答案 2 :(得分:1)
虽然没有明确说明,但实际上你必须确保包含controllerWillChangeContent的委托方法:和controllerDidChangeContent:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
[self.tableView beginUpdates];
}
和
- (void)controllerDidChangeContent:(BSFetchedResultsController *)controller {
@try {
[self.tableView endUpdates];
}
@catch (NSException * e) {
NSLog(@"caught exception: %@: %@", [e name], [e description]);
}
@finally { }
}
NSFRC可以在任何一次更改期间触发多个控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath:方法,因此不要指望表行在每个控制器之后直接更新。它们只会在您在桌面上调用endUpdates后更新。
答案 3 :(得分:0)
在MAIN主题中调用UI更新逻辑解决了我的问题([cell setNeedsLayout]
& [cell setNeedsDisplay]
对我不起作用):
...
case NSFetchedResultsChangeUpdate:
dispatch_async(dispatch_get_main_queue(), {
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
});
break;
...
更多(不是关于这个问题,但会有用),如果有newIndexPath
,你最好选择使用...
case NSFetchedResultsChangeUpdate:
dispatch_async(dispatch_get_main_queue(), {
NSIndexPath * targetIndexPath = (newIndexPath ?: indexPath)
[self configureCell:[tableView cellForRowAtIndexPath:targetIndexPath]
atIndexPath:targetIndexPath];
});
break;
...
:
{{1}}