示例应用程序有一个表格视图,该视图由UITableViewDiffableDataSource
从NSFetchedResultsController
. 您可以通过按加号按钮将字母表添加到表格视图中。为了实现数据源,我使用了这篇文章。问题是,当我向 Core Data 添加新项目时,会将NSFetchedResultsController
临时 ID 馈送到单元提供程序。当我向下滚动并且单元格提供程序必须重用单元格时,它无法获取具有临时 ID 的托管对象。但是,当项目被添加到屏幕上的表格视图区域时,它不会发生。
lazy var fetchedResultsController: NSFetchedResultsController<Item> = {
let fetchRequest: NSFetchRequest<Item> = Item.fetchRequest()
let sort = NSSortDescriptor(key: #keyPath(Item.name), ascending: true)
fetchRequest.sortDescriptors = [sort]
let controller = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: moc,
sectionNameKeyPath: nil,
cacheName: nil
)
controller.delegate = self
return controller
}()
func configureDiffableDataSource() {
let diffableDataSource = UITableViewDiffableDataSource<Int, NSManagedObjectID>(tableView: tableView) { (tableView, indexPath, objectID) -> UITableViewCell? in
guard let object = try? self.moc.existingObject(with: objectID) as? Item else {
// Crash happens here.
fatalError("Managed object should be available.")
}
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_id", for: indexPath)
cell.textLabel?.text = object.name
return cell
}
self.diffableDataSource = diffableDataSource
tableView.dataSource = diffableDataSource
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
guard let dataSource = tableView?.dataSource as? UITableViewDiffableDataSource<Int, NSManagedObjectID> else {
assertionFailure("The data source has not implemented snapshot support while it should.")
return
}
var snapshot = snapshot as NSDiffableDataSourceSnapshot<Int, NSManagedObjectID>
let currentSnapshot = dataSource.snapshot() as NSDiffableDataSourceSnapshot<Int, NSManagedObjectID>
let reloadIdentifiers: [NSManagedObjectID] = snapshot.itemIdentifiers.compactMap { itemIdentifier in
guard let currentIndex = currentSnapshot.indexOfItem(itemIdentifier), let index = snapshot.indexOfItem(itemIdentifier), index == currentIndex else {
return nil
}
guard let existingObject = try? controller.managedObjectContext.existingObject(with: itemIdentifier), existingObject.isUpdated else { return nil }
return itemIdentifier
}
snapshot.reloadItems(reloadIdentifiers)
let shouldAnimate = tableView?.numberOfSections != 0
dataSource.apply(snapshot as NSDiffableDataSourceSnapshot<Int, NSManagedObjectID>, animatingDifferences: shouldAnimate)
}
在保存到 Core Data 后立即添加try! fetchedResultsController.performFetch()
可以解决问题,但是,它是一种蛮力解决方案,会导致对controller(_:didChangeContentWith:)
委托方法的双重调用,有时还会导致双重动画。在这种情况下,获取应该自动发生。我想知道,为什么单元格提供程序无法获取数据以及如何以有效的方式解决此问题。
@objc func handleAdd() {
// Add item to Core Data.
let context = moc
let entity = Item.entity()
let item = Item(entity: entity, insertInto: context)
item.name = "\(letters[counter])" // Adds letters of the alphabet.
counter += 1
try! context.save()
// Manually fetching right after saving doesn’t seem efficient.
try! fetchedResultsController.performFetch()
}