ios - How to get a diffable snapshot from an NSFetchResultsController in iOS 13?
问题描述
So here we are in WWDC 2019 video 230, and starting at about minute 14 it is claimed that NSFetchedResultsController now vends an NSDiffableDataSourceSnapshot, so we can just apply it directly to a diffable data source (UITableViewDiffableDataSource).
But this is not quite what they say, or what we get. What we get, in the delegate method controller(_:didChangeContentWith:)
, is an NSDiffableDataSourceReference. How do we get from this to an actual snapshot, and what should my diffable data source generic types be?
解决方案
WWDC 视频暗示我们应该用 和 的泛型类型声明数据String
源NSManagedObjectID
。那对我不起作用;我可以通过动画和行更新获得合理行为的唯一方法是使用自定义值对象作为数据源的行标识符。
使用NSManagedObjectID
作为项目标识符的快照的问题在于,尽管获取的结果委托被通知与该标识符关联的托管对象的更改,但它提供的快照可能与我们可能应用的前一个快照没有什么不同数据源。当底层数据发生变化并解决单元格更新问题时,将此快照映射到使用值对象作为标识符的快照上会产生不同的哈希值。
考虑一个待办事项列表应用程序的数据源,其中有一个包含任务列表的表视图。每个单元格显示一个标题和任务是否完成的一些指示。值对象可能如下所示:
struct TaskItem: Hashable {
var title: String
var isComplete: Bool
}
数据源呈现这些项目的快照:
typealias DataSource = UITableViewDiffableDataSource<String, TaskItem>
lazy var dataSource = DataSource(tableView: tableView) { tableView, indexPath, item in {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = item.title
cell.accessoryType = item.isComplete ? .checkmark : .none
return cell
}
假设一个获取的结果控制器,它可以被分组,委托传递一个快照类型为String
和NSManagedObjectID
。可以将其操作为String
和TaskItem
(用作行标识符的值对象)的快照以应用于数据源:
func controller(
_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference
) {
// Cast the snapshot reference to a snapshot
let snapshot = snapshot as NSDiffableDataSourceSnapshot<String, NSManagedObjectID>
// Create a new snapshot with the value object as item identifier
var mySnapshot = NSDiffableDataSourceSnapshot<String, TaskItem>()
// Copy the sections from the fetched results controller's snapshot
mySnapshot.appendSections(snapshot.sectionIdentifiers)
// For each section, map the item identifiers (NSManagedObjectID) from the
// fetched result controller's snapshot to managed objects (Task) and
// then to value objects (TaskItem), before adding to the new snapshot
mySnapshot.sectionIdentifiers.forEach { section in
let itemIdentifiers = snapshot.itemIdentifiers(inSection: section)
.map {context.object(with: $0) as! Task}
.map {TaskItem(title: $0.title, isComplete: $0.isComplete)}
mySnapshot.appendItems(itemIdentifiers, toSection: section)
}
// Apply the snapshot, animating differences unless not in a window
dataSource.apply(mySnapshot, animatingDifferences: view.window != nil)
}
初始performFetch
inviewDidLoad
更新没有动画的表视图。此后的所有更新,包括仅刷新单元格的更新,都适用于动画。
推荐阅读
- python - Python将jpg插入Word文档中的特定位置
- asp.net-mvc-5 - ASP.Net Core 无法保存模型
- python - 如何键入提示 getter 只允许 dict 键?
- jms - IBM Integration Bus 通过 SSL 从 Apache ActiveMQ 读取队列
- python - 使用python / Django,有没有办法在具有特定线长和角度的矩形框(特别是土地地图)中绘制形状
- amazon-web-services - 403 Forbidden 仅适用于某些文件夹/对象
- logging - Uvicorn/FastAPI 重复记录
- symfony - 一个实体和多个实体之间的双向多对多
- flutter - 如何将点击功能添加到 listview.builder 项目?
- python-3.x - 如何在不安装 python 32bit 的情况下在 windows 32bit 中运行使用 python 64bit 制作的应用程序?