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

Posted

技术标签:

【中文标题】使用 NSManagedContextDidSaveNotification 实现的 NSFetchedResultsController 委托的 UITableViewCell 标题显示绘图问题【英文标题】:UITableViewCell header display drawing issue with NSFetchedResultsController delegate using NSManagedContextDidSaveNotification implementation 【发布时间】:2015-03-20 23:15:36 【问题描述】:

我已经实现了一个带有NSManagedContextDidSaveNotificationNSFetchedResultsController 委托,以将托管对象更改从另一个NSManagedObjectContext 推送到一个公共NSPersistentStoreCoordinator

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

这基本上是一个显示/绘图错误,其中节标题被绘制到单元格中。该标题和单元格是实际有效的标题和单元格,它们出现在更下方。

这只发生在第一次批量创建托管对象时。如果我重新启动应用程序并且已导入托管对象,则控制器会显示一切正常,因此很可能与导入过程有关,这只是典型的 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];
    );

【问题讨论】:

【参考方案1】:

您是否尝试如下实现/调整您的代码?

NSFetchedResultsChangeUpdate 部分更改时,请致电- reloadSections:withRowAnimation:- moveSection:toSection:NSFetchedResultsChangeMove 发生在部分更改时, - moveRowAtIndexPath:toIndexPath:NSFetchedResultsChangeMovecontroller:didChangeObject: 触发时,并且 - reloadRowsAtIndexPaths:withRowAnimation:NSFetchedResultsChangeUpdatecontroller:didChangeObject: 触发时。

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

最后,您是否尝试过使用 childContexts?

【讨论】:

很好,我的NSFetchedResultsControllerDelegate 方法在NSManagedObjectContextDidSaveNotification 调度线程上被调用,这不是主线程。我确保在主线程上调用我的NSManagedObjectContextDidSaveNotification 处理程序方法,一切都得到了修复。谢谢! 太棒了!乐于助人。【参考方案2】:

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

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

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

【讨论】:

我删除了它,所以它不那么令人困惑,但它是我尝试添加和删除的一个非因素。我也曾在reloadData 之前尝试过performFetch:error。不过,我并没有尝试先设置为nil。会尽量保持发布。 我刚刚编辑的高度操作代码是因为表格内容在初始导入后没有正确调整,所以为了安全起见。我知道我不需要这样做。 我尝试使用performSelector:withObject:afterDelay_frc = nil; [self setupFrc]; [_tableView reloadData];,但效果不佳。 为什么有 setupFrc?使用 Apple 在模板中提供的普通代码,并延迟创建 FRC。 我不知道您在谈论哪个香草代码,但我认为这不会改变任何事情。设置获取请求并实例化 frc 并不是真正的问题,除非您认为这就是单元格绘制混乱的原因。

以上是关于使用 NSManagedContextDidSaveNotification 实现的 NSFetchedResultsController 委托的 UITableViewCell 标题显示绘图问题的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)