NSFetchedResultsController -controllerDidChangeContent: - 尝试使用userInfo为单元格创建两个动画

时间:2014-03-30 14:01:59

标签: ios iphone objective-c uitableview nsfetchedresultscontroller

当我在我的TabbarController中的两个选项卡之间切换到同时包含NSFetchResultsController的Viewcontrollers时。在我的一个viewcontrollers中更新东西时,我得到以下Core Data Error:

CoreData: error: Serious application error.  An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:.  Attempt to create two animations for cell with userInfo (null)

如果我尝试更新数据,我的tableview会变白并崩溃。

My -controllerDidChangeContent:看起来像这样:

  1. Viewcontroller(我更新数据的viewcontroller)。

    -(void)controllerWillChangeContent:(NSFetchedResultsController*)controller
    {
    [self.productDetailTableView beginUpdates];
    }
    
    
    -(void)controllerDidChangeContent:(NSFetchedResultsController*)controller
    {
    [self.productDetailTableView endUpdates];
    }
    
    
    -(void)controller:(NSFetchedResultsController*)controller didChangeSection:(id<NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
    {
    
    
    UITableView *tableView = self.productDetailTableView;
    
    switch (type) {
        case NSFetchedResultsChangeInsert:
            [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex]
                                                    withRowAnimation:UITableViewRowAnimationFade];
            break;
    
        case NSFetchedResultsChangeDelete:
            [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.productDetailTableView;
    
    NSIndexPath *tvIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(indexPath.section + 1)];
    NSIndexPath *tvNewIndexPath = [NSIndexPath indexPathForRow:newIndexPath.row inSection:(newIndexPath.section + 1)];
    
    indexPath = tvIndexPath;
    newIndexPath = tvNewIndexPath;
    switch (type) {
        case NSFetchedResultsChangeInsert:
    
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                                                            withRowAnimation:UITableViewRowAnimationFade];
    
            break;
    
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                                                            withRowAnimation:UITableViewRowAnimationFade];
    
            break;
    
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray
                                               arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
            [tableView insertRowsAtIndexPaths:[NSArray
                                               arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
            break;
    }
    }
    
  2. Viewcontroller(此Viewcontroller,其中TableView变为白色并发生错误)。

    -(void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    
     [self.cartTableView beginUpdates];
     }
    
    
    -(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    
    UITableView *tableView = self.cartTableView;
    
    switch(type) {
    
        case NSFetchedResultsChangeInsert:
              [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
            break;
    
        case NSFetchedResultsChangeDelete:
            [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
            break;
    
        case NSFetchedResultsChangeUpdate:
            [self configureCell:(ProductCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;
    
        case NSFetchedResultsChangeMove:
            [tableView deleteRowsAtIndexPaths:[NSArray
                                               arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
            [tableView insertRowsAtIndexPaths:[NSArray
                                               arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];
            break;
     }
    }   
    
    -(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
    
     UITableView *tableView = self.cartTableView;
    
    switch(type) {
    
        case NSFetchedResultsChangeInsert:
            [tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
    
        case NSFetchedResultsChangeDelete:
            [tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
            break;
        }
        }
    
    
    -(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    
     [self.cartTableView endUpdates];
     }
    
  3. 如何解决这个问题并获得“尝试使用userInfo为单元格创建两个动画”消失,这是什么原因发生的?

4 个答案:

答案 0 :(得分:3)

这不是核心数据问题,并且与核心数据完全无关。这是一个tableview问题。 CoreData正在调用你的委托方法,并且在那里UIKit抛出一个异常,因为你试图动画两次相同的单元格。

  1. 为所有异常设置符号断点,并尝试复制此行为。请注意,当CoreData解决合并冲突时,它将抛出完全正常的异常,但调试器将停止这些异常。继续前进直到你的tableview异常。这将缩小表视图的问题范围。

  2. 查看驱动细胞动画的数据。从您的代码看,似乎您在动画之前修改了索引路径。这可能是问题的一部分。想象一下,如果NSFetchedResultsChangeMovetvIndexPath等于tvNewIndexPath。这将产生你正在描述的内容。

  3. CoreData绝对不是问题,它是你的tableview动画。 CoreData只是信使。

答案 1 :(得分:2)

要看的事情:

a)你应该确保NSFetchResultsController的委托指向正确的视图控制器;如果两个FRC指向同一个视图控制器并导致双动画

,则可能设置错误

b)确保通过设置断点来调用视图控制器的controllerWillChangeContent一次(或两次)

c)注意两个视图控制器的didChangeObject代码有何不同?确保您知道indexPaths正在正确设置

如果没有效果: - 关于如何设置FRC的邮政编码 - 发布有关如何将对象插入NSManagedObjectContext的代码

答案 2 :(得分:2)

我刚刚在我公司的代码中解决了这个问题。我必须编写一个类来清理fetch结果控制器的输出,以便在表视图中使用。

NSFetchResultsController API与UITableViewController API的匹配非常好,以至于它们似乎是如何协同工作的,但 IT&#39; SA陷阱!表格view无法处理获取结果控制器可以生成的所有更新组合。

在我们的案例中,我们显示按上次打开日期排序的文档列表。当您打开文档时,其dateLastOpened字段会更改(触发Update通知),并且其在排序列表中的顺序会更改(触发Move通知)。如果您尝试在同一个tableView更新中应用这两个更改,则会触发臭名昭着的断言。

所以我的过滤器类监听NSFetchResultsController委托回调并记录索引路径。完成更新后,它会将Move个事件转换为DeleteAdd,并删除触及已移动或已删除索引路径的所有Update个事件。生成的更改集将作为批处理应用于UITableView

答案 3 :(得分:1)

由于您使用的是相同的NSManagedObject,因此当数据发生更改时,可能会在两个控制器上调用didChangeObject。

因此,如果您在第一个视图控制器上并添加一个条目,则会调用此行:

[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]                                                      withRowAnimation:UITableViewRowAnimationFade];

当这个动画发生时,第二个控制器上的这一行也会被调用:

[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone];

我认为这就是你收到错误的原因。您应该选中所选的标签,并且只在当前选择的控制器上拨打beginUpdatesendUpdatesinsertRowsAtIndexPathdeleteRowsAtIndexPath

您可以使用以下网址获取当前选定的标签:tabBarController.selectedIndex