重用单元格时,UITableViewDiffableDataSource 单元格提供程序无法获取具有临时 ID 的新添加的托管对象

问题描述

The sample app 有一个由 UITableViewDiffableDataSource 提供支持的表视图,它从 NSFetchedResultsController 获取数据。您可以通过按加号按钮将字母表中的字母添加到表格视图中。为了实现数据源,我使用了this article。问题是,当我向 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:) 委托方法的双重调用,有时还会导致双重动画。在这种情况下,提取应该自动发生。我想知道为什么 Cell Provider 无法获取数据以及如何以有效的方式解决此问题。

@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()
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)