10

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 而无需重新加载行、节或表。

4

4 回答 4

3

您应该调用[cell setNeedsLayout]or/and[cell setNeedsDisplay]以使单元格得到刷新,具体取决于您的单元格的实现。

如果你像我们通常做的那样组成子视图的单元格,你依赖- layoutSubviews,所以你应该调用[cell setNeedsLayout].

如果你直接用 绘制单元格– drawRect:,你应该调用[cell setNeedsDisplay].

如果你同时使用构图和绘图,你应该调用两者。

于 2011-05-13T04:58:48.740 回答
1

虽然您确实不需要重新加载单元格来进行更改,但您必须记住 iPhone 会尽可能多地缓存视图绘图。新配置单元格后,您需要在单元格上调用 setNeedsDisplay 以触发重绘。

于 2009-10-12T14:38:34.963 回答
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 可以在任何一次更改期间触发多个 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 方法,因此不要期望表格行在每个更改之后立即更新。它们只会在您在桌子上调用 endUpdates 后更新。

于 2011-02-09T13:13:53.080 回答
0

在主线程中调用 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;
...
于 2015-04-21T15:05:50.247 回答