由于 NSManagedObjectContext 保存而更新 NSFetchedResultsController 时延迟 UITableView 更新

Posted

技术标签:

【中文标题】由于 NSManagedObjectContext 保存而更新 NSFetchedResultsController 时延迟 UITableView 更新【英文标题】:Delay UITableView update when NSFetchedResultsController is updated due to NSManagedObjectContext save 【发布时间】:2010-07-15 01:34:57 【问题描述】:

我正在使用带有 NSFetchedResultsController 的谓词对 UITableView 进行排序。但是,当我在详细视图中进行更改并保存它时,我更改的对象立即被放置在 UITableView 中的新位置。

我有类似于 Mail 中消息视图的向上/向下按钮。这种行为会打乱项目的顺序,我想延迟这种变化,直到用户出于 UX 原因退出 UITableView。我在 UITableViewController 中实现了 NSFetchedResultsControllerDelegate 方法。有没有简单/聪明的方法来做到这一点?

编辑:下面是在 UITableViewController 中实现的 NSFetchedResultsControllerDelegate 方法。

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller 
    [self.tableView beginUpdates];



- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
           atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type 

    switch(type) 
        case NSFetchedResultsChangeInsert:
            [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
            break;
    



- (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 insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                             withRowAnimation:UITableViewRowAnimationFade];
            break;
    



- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
    [self.tableView endUpdates];

【问题讨论】:

这对我来说听起来很奇怪。你能发布一些与更新详细视图和 UITableView 的代码有关的代码吗? 设置是当自定义 UIControl 更改状态时,通过 KVO 通知详细视图控制器,将更改 NSManagedObject,并在 NSManagedObjectContext 上触发保存。当 NSManagedObjectContext 保存时,我认为它会通知 NSFetchedResultsController 事情发生了变化。 NSFetchedResultsController 然后告诉它的委托发生了什么变化。我有在上面的表视图控制器中实现的 NSFetchedResultsControllerDelegate 方法。谢谢 【参考方案1】:

问题不在于NSFetchedResultsControllerDelegate 方法。这些方法只有在表格可见时更改数据模型时才会发挥作用。

您的问题是您在表格视图处于屏幕外且处于非活动状态时更改了数据。表格视图旨在自动显示给它们的数据模型。当您从详细视图返回到 tableview 时,它会自动重新加载其数据,这些数据现在包含在详细视图中所做的更改。无论您如何向表格提供数据,它都会这样做。

阻止表格显示更改直到您希望它显示的唯一方法是在您希望更改出现之前不将数据添加到数据模型中。您必须将数据从详细视图传递回非托管对象中的表,并在您希望它出现时在表视图控制器中创建托管对象。

但是,从 UI 设计的角度来看,我建议您重新考虑您的设计。如果您在重新加载后才更改表,您将如何向用户发出您将进行更改的信号?用户界面会无缘无故地突然更新吗?用户是否必须启动它?如果他们忘记了怎么办?

我认为用户会期望在详细视图中所做的任何更改都会立即反映在 tableview 中,因为这实际上是所有 tableview-detailView 配对的工作方式。例如,如果您更改地址簿的联系人详细信息中的联系人姓名,当您返回联系人列表时,该联系人会立即反映。

【讨论】:

谢谢。我正在考虑制作一个类似于您所说的暂存区,并在正确的时间通过暂存区的更改。至于 UI 透视图,表格按每个项目的评分排序。当评级发生变化时,它会完全改变您在列表中的位置,因此在更改评级之前和之后使用下一个/上一个按钮会产生不同的结果。此外,我喜欢能够处理这样的用例,即用户能够浏览所有未评级的项目以对它们进行评级而不会失去他的位置。我并不完全相信这两种设计都是完全正确的。你怎么看? 如果我理解您的设计,我想您可能希望在您的数据模型中添加一个未显示的评级,用于对表格进行排序。当用户进行更改时,您会更改显示的评分,但不会更改未显示的评分,以便对象保持与更改前相同的 UI 顺序。然后你可以有一个“排序表”按钮,它将显示的评分复制到未显示的评分,并将所有内容按正确的顺序排列。 啊,这很有道理。谢谢!【参考方案2】:

我使用的一个技巧是在表格视图控制器消失时调用 beginUpdates,然后在它出现时调用 endUpdates:

- (void)viewWillAppear:(BOOL)animated
    [self.tableView endUpdates];
    [super viewWillAppear:animated];


- (void)viewDidDisappear:(BOOL)animated
    [super viewDidDisappear:animated];
    [self.tableView beginUpdates];

这样,获取控制器可以继续工作,您可以继续将更新用于其他事情。

【讨论】:

以上是关于由于 NSManagedObjectContext 保存而更新 NSFetchedResultsController 时延迟 UITableView 更新的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 和 NSManagedObjectContext

多个 NSManagedObjectContext 或传递它?

在后台线程上初始化 NSManagedObjectContext 和 NSPersistentStoreCoordinator

Restkit 核心数据与 NSManagedObjectContext 集成

将 NSManagedObjectContext 上下文传递给 ViewController 是 nil

父/子 NSManagedObjectContext 不起作用