UITableViewCell header 使用 NSManagedContextDidSaveNotification 实现的 NSFetchedResultsController 委托显示绘图问题

UITableViewCell header display drawing issue with NSFetchedResultsController delegate using NSManagedContextDidSaveNotification implementation

我已经使用 NSManagedContextDidSaveNotification 实施了一个 NSFetchedResultsController 委托,以从连接到公共 NSPersistentStoreCoordinator 的另一个 NSManagedObjectContext 推送托管的 objects 更改。

第一次批量导入托管objects时,调用NSFetchedResultsControllerDelegate方法后,单元格绘图的结果包含如下所示的部分:

这基本上是一个 display/drawing 错误,其中 header 部分被绘制到单元格中。 header 和 cell 是实际有效的 header 和出现在更下方的 cell。

它只会在第一次管理时发生 objects 是批量创建的。如果我重新启动应用程序并且管理的 objects 已经导入,控制器显示一切正常,所以它可能与导入过程有关,这只是典型的 NSFetchedResultsControllerDelegate 实现(粘贴在下面):

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [_tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:
     (id <NSFetchedResultsSectionInfo>)sectionInfo 
     atIndex:(NSUInteger)sectionIndex 
     forChangeType:(NSFetchedResultsChangeType)type {
    switch(type) {
        case NSFetchedResultsChangeInsert:
            [_tableView insertSections:
                [NSIndexSet indexSetWithIndex:sectionIndex]
                withRowAnimation:UITableViewRowAnimationFade]; break;
        case NSFetchedResultsChangeDelete:
            [_tableView deleteSections:
                [NSIndexSet indexSetWithIndex:sectionIndex]
                withRowAnimation:UITableViewRowAnimationFade]; break;
        case NSFetchedResultsChangeUpdate:
            NSLog(@"change section"); break;
        case NSFetchedResultsChangeMove:
            NSLog(@"move setion"); break;
    }
}
- (void)controller:(NSFetchedResultsController *)controller 
    didChangeObject:(id)anObject atIndexPath:
    (NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
  newIndexPath:(NSIndexPath *)newIndexPath {
    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];
            NSLog(@"change object"); break;
        case NSFetchedResultsChangeMove:
            [_tableView deleteRowsAtIndexPaths:
              [NSArray arrayWithObject:indexPath] 
              withRowAnimation:UITableViewRowAnimationFade];
            [_tableView insertRowsAtIndexPaths:
              [NSArray arrayWithObject:newIndexPath] 
              withRowAnimation:UITableViewRowAnimationFade];
            NSLog(@"move object"); break;
    }
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
    [_tableView endUpdates];
}

问题是我尝试通过检测何时是第一批导入并调用它们来使用 needsLayoutreloadData 实现刷新并不能解决此显示问题。哈普!

编辑:

 - (void)setupFetchedResultsController {
    NSFetchRequest *fr = [NSFetchRequest fetchRequestWithEntityName:@"Object"];
    NSSortDescriptor *sd1 = [NSSortDescriptor sortDescriptorWithKey:@"attribute" ascending:YES];
    NSSortDescriptor *sd2 = [NSSortDescriptor sortDescriptorWithKey:@"attribute2" ascending:YES];
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self.relationship.attribute3 == true"];
    [fr setPredicate:predicate];
    [fr setSortDescriptors:@[sd1, sd2]];
    _frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fr managedObjectContext:_moc sectionNameKeyPath:@"attribute" cacheName:nil];
    [_frc setDelegate:self];
    NSError *error; if (![_frc performFetch:&error]) NSLog(@"%@", error.description);
}

编辑:这是解决问题的代码:

- (void)subscribeToNotifications {
    [[NSNotificationCenter defaultCenter] addObserver:self 
      selector:@selector(mergeManagedObject:) 
      name:NSManagedObjectContextDidSaveNotification
      object:nil];
}
- (void)mergeManagedObject:(NSNotification *)notification {
    dispatch_async(dispatch_get_main_queue(), ^{
        // This block needed to be sent on the main thread!
        [_managedObjectContext 
          mergeChangesFromContextDidSaveNotification:notification];
    });
}

尝试使用 reloadData 解决方案时,您可以先 nil 取出获取的结果控制器,然后延迟重新创建它。然后它应该从头开始正确重建所有内容。

此外,您应该在 结束 table 视图更新后消除高度操作 。在极少数情况下,您必须手动调整 table 视图的内容高度 - 您认为为什么必须在此处进行调整?

此外,您应该检查控制器中所有会影响单元格和部分高度的方法。

您是否尝试过 implementing/adjusting 您的代码如下?

  • NSFetchedResultsChangeUpdate 部分发生变化时调用 - reloadSections:withRowAnimation:
  • - moveSection:toSection:NSFetchedResultsChangeMove 发生在部分更改时,
  • - moveRowAtIndexPath:toIndexPath:NSFetchedResultsChangeMovecontroller:didChangeObject: 上触发时,
  • - reloadRowsAtIndexPaths:withRowAnimation:NSFetchedResultsChangeUpdatecontroller:didChangeObject: 上触发时。

如果这没有帮助,请进入这些方法并查看调用它们的线程。通常,在主线程以外的线程上调用视图更新时会发生视图错误。

最后,您尝试使用 childContexts 了吗?