NSFetchedResultsControllerDelegate的“ChangeUpdate”行为是否已损坏?

时间:2009-08-25 06:40:19

标签: iphone uitableview core-data nsfetchedresultscontroller

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 个答案:

答案 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}}