是否可以从 NSFetchedResultsController 获取最新快照
Posted
技术标签:
【中文标题】是否可以从 NSFetchedResultsController 获取最新快照【英文标题】:Is it possible to source an up-to-date snapshot from an NSFetchedResultsController 【发布时间】:2020-06-01 23:11:17 【问题描述】:从 ios 13 开始,使 UITableView
与 NSFetchedResultsController
保持同步的最简单方法似乎是使用快照。
每当 managedObjectContext 报告添加、删除或更新时,NSFetchedResultsController 都会向其委托提供快照引用。使用快照 (NSDiffableDataSourceSnapshot
) 时,只需实现一种 FRC 委托方法:controller(_:didChangeContentWith:)
。为了使该委托方法工作,UITableViewDiffableDataSource 和快照必须输入<String, NSManagedObjectID>
。
它可以工作mostly。
但是如果需要更新整个表怎么办?使用 tableView.reloadData()
或 frc.performFetch()
似乎是反模式。
编辑
我手动构建了一个快照,并在必要时调用 apply。但是由于我的快照是基于 NSFetchedResultsSectionInfo 对象的,所以我似乎在复制 FRC 已有的内容:Hashable 部分标题和 Hashable NSManagedObjectIDs
【问题讨论】:
SmallTalk,我不明白你的问题: UITableView 和 NSFetchedResultsController 工作绝对完美。 (他们当然会这样做,否则许多主要应用程序会崩溃:-))我对您为什么还要使用快照感到困惑?为什么?是什么原因?你能帮我理解吗? 嗨 Fattie,谢谢支持。我的问题是:如果需要更新整个 UITableView,生成新的NSDiffableDataSourceSnapshot
的最佳做法是什么?我希望我能从 FRC 中“抓住它”。 FRC 现在可以提供 NSDiffableDataSourceSnapshotReference。但看来我必须通过自己的快照手动构建。由于我的快照是从 NSFetchedResultsSectionInfo 对象构建的,因此我似乎在复制 FRC 已有的内容:可散列的部分标题和可散列的 NSManagedObjectIDs
我使用快照是因为我能够消除大约 6 个 FRC 委托方法,而只使用一个。正如 Apple 的“controller(_:didChangeContentWith:) 文档”所说:“如果实现了此方法,则不会调用其他委托方法。”所以 UITableViewDiffableDataSource+UITableView 超级性感,有漂亮的动画,并且消除了大量的样板代码。但我仍在试图理解为什么我必须“滚动我自己”的快照,如果 FRC 可以自己生成一个。
smalltalk @smallTalk,非常感谢您提供的信息 - 我将彻底调查您所说的一切!
【参考方案1】:
对于我之前(已删除)的答案,我深表歉意。快照与 Core Data 上下文无关。
NSFetchedResultsController
与 Core Data
结合使用的目的是在保存 NSManagedObjectContext
时更新 UI。
为了能够控制 diffable 数据源的动画(解决荒谬的行为),您必须继承 UITableViewDiffableDataSource
并添加属性 animatingDifferences
。进一步在子类中采用NSFetchedResultsControllerDelegate
(不是在视图控制器中)。
class DiffableCoreDataSource: UITableViewDiffableDataSource<String,NSManagedObjectID>
var animatingDifferences = false
extension DiffableCoreDataSource : NSFetchedResultsControllerDelegate
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference)
apply(snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>, animatingDifferences: animatingDifferences)
animatingDifferences = true // set it to the default
在视图控制器中,将 FRC 的 delegate
设置为子类,假设有一个代表 DiffableCoreDataSource
的 dataSource
属性
frc.delegate = dataSource
如果记录更新,则在保存上下文之前将dataSource.animatingDifferences
设置为false
。
要重新加载整个表视图调用frc.performFetch()
。永远不要在表格视图上调用reloadData()
。
【讨论】:
您好 Vadian,感谢您抽出宝贵时间回答。我将对此进行实验。最后一句真的是我所追求的;如果performFetch()
在功能上等同于创建自己的NSDiffableDataSourceSnapshot
并在需要更新整个表时应用它,那么这将是公认的答案。 (与 CoreData 和 FRC 一起管理快照似乎是一种不直观的方法。但这可能是 Apple 目前为我们提供的。)
“永远不要在表格视图上调用 reloadData()。”我不能同意。重新加载数据是使表格显示单元格内容更改的唯一方法(因为 Hashable/Equatable 的定义方式)不构成“不同”单元格。此处的解决方案不应对同时更改记录和创建/删除另一个记录的可能性。例如,请参阅***.com/questions/62267256/…。
@matt reloadData()
在UITableViewDiffableDataSource
驱动的上下文中是什么?标准实现中没有cellForRowAt
@vadian 这就是我对链接问题的回答所证明的。
@matt 我理解,但是对于 Core Data 和 NSFetchedResultsControllerDelegate,您通常不会自己 apply
快照。上下文将调用委托方法的更改通知 FRC。在我看来,控制animatingDifferences
参数比重新加载表格视图更有效。【参考方案2】:
TL;DR:即使 NSFetchedResultsController 能够在 managedObjectContext 报告添加、删除和更新时为其委托提供最新的快照引用,但(至少目前)无法以编程方式访问直接来自 FRC 的快照。
正如 vadian 建议的那样,使用 FRC 实例方法 performFetch()
将最新快照应用于 DiffableData 支持的 UITableview
中的所有单元格。这是一种未记录的快照管理方法。但它可以单独使用 FRC 提供的快照,而不必编写独立的快照。更少的代码,以及用于快照更新的“单一真实来源”。
根据其他人的反馈,缺点是apply(_: animatingDifferences:)
方法中的animatingDifferences
参数如果设置为true 会触发错误。两个可重现的错误是第一次应用快照时表根本无法加载。如果尝试使用trailingSwipeActionsConfigurationForRowAt
方法或其他方法删除记录,则另一个错误是应用程序崩溃。因此,使用这种后门方法的唯一方法是在所有情况下将该 bool 设置为 false。这意味着,嗯,没有动画。
【讨论】:
闲聊感谢这个sn-p。现在,要更新单元格,我使用的是 NSManagedObjectContext。我希望这种方法是正确的:让 model = self.viewContext.object(with: objectID) as! MyModel cell.configureCell(with: model, animated: false) 您好 AgentX 大师,如果您遇到问题,我会将其作为单独的问题发布。 1) 据我了解,在 MOCsave
操作之前,objectID 不会永久存在,因此请确保在配置任何单元之前已调用 save。其次,UITableViewDiffableDataSource
init 方法直接出售托管对象,所以我推荐该路径。见wwdcbysundell.com/2019/diffable-data-sources-first-look。 (在这种情况下,在 init 闭包中找到的托管对象来自 performFetch()
中生成的“秘密”NSFetchedResultsController snapshot
,如上。以上是关于是否可以从 NSFetchedResultsController 获取最新快照的主要内容,如果未能解决你的问题,请参考以下文章