【问题标题】:Is NSFetchedResultsControllerDelegate 'ChangeUpdate' behavior broken?NSFetchedResultsControllerDelegate 'ChangeUpdate' 行为是否被破坏?
【发布时间】:2010-11-22 12:31:29
【问题描述】:

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 上调用,它能够检索最新的 'name' 值并将其设置在单元格的 textLabel 中。除非我重新加载,否则该名称不会在单元格中呈现。即使我只是单击单元格,也会显示名称。请注意,要重新创建此行为,您必须创建一个新的托管对象,然后为其命名,使其在 NSFetchedResultsController 中排序为 FIRST。这样,NSFetchedResultsChangeMove 就不会触发(因为它会重新加载该部分,所以它确实有效)。

我错过了什么还是这是预期的行为? reloadRowsAtIndexPaths 的“讨论”让我相信我应该能够简单地设置单元格的 textLabel 而无需重新加载行、节或表。

【问题讨论】:

  • 这应该可以工作。我也在做同样的事情。对于属性的更改,当调用 save: 时,MOC 通知会消失。 NSFetchedResultsController 看到更改并调用我的 configureCell:atIndexPath 方法。在那里我抓取值并填充单元格,然后单元格立即更新。发布您的 configureCell 代码。

标签: iphone uitableview core-data nsfetchedresultscontroller


【解决方案1】:

您应该调用[cell setNeedsLayout] 或/和[cell setNeedsDisplay] 以使单元格得到刷新,具体取决于您的单元格的实现。

如果你像我们通常那样组成子视图的单元格,你依赖- layoutSubviews,所以你应该调用[cell setNeedsLayout]

如果你直接用– drawRect:绘制单元格,你应该调用[cell setNeedsDisplay]

如果你同时使用构图和绘图,你应该同时调用。

【讨论】:

    【解决方案2】:

    虽然您确实不需要重新加载单元格以使更改发生,但您必须记住 iPhone 尽可能多地缓存视图绘图。新配置单元格后,您需要在单元格上调用 setNeedsDisplay 以触发重绘。

    【讨论】:

    • 此时 - 获取屏幕上单元格的最佳方法是什么? tableview:cellForRowAtIndexPath: 创建一个新单元格,而我想在屏幕上获取对单元格的引用......对吗?
    • 通常不需要,对于您正在配置的任何单元格,您可以调用 setNeedsDisplay。如果它是一个新单元,它没有缓存,所以不用担心,如果你正在重新配置,它会重绘。但是,如果您真的想知道哪些屏幕是可见的,请查看我对以下内容的回复:stackoverflow.com/questions/996515/…
    【解决方案3】:

    虽然没有明确说明,但您实际上必须确保包含 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 可以在任何一次更改期间触发多个 controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: 方法,因此不要期望表格行在每个更改之后立即更新。它们只会在您对桌子调用 endUpdates 后更新。

    【讨论】:

      【解决方案4】:

      在主线程中调用 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;
      ...
      

      【讨论】:

      • 那些事件已经在主线程上,你正在做的是分流到下一个事件循环,虽然 GCD 不是 100% 擅长,延迟后执行更可靠。
      猜你喜欢
      • 2017-08-13
      • 2018-02-26
      • 2019-06-15
      • 2013-12-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-25
      • 2016-07-11
      • 1970-01-01
      相关资源
      最近更新 更多