【发布时间】:2021-09-25 12:59:27
【问题描述】:
到目前为止,根据提供的信息,这里是几乎适用于NSFetchedResultsController + UICollectionView 的代码 sn-ps
- https://developer.apple.com/videos/play/wwdc2018/225(36:50开始)
- How to order moves, inserts, deletes, and updates in a UICollectionView performBatchUpdates block?
请注意,有 2 个[BlockOperation],因为reloadItems 和moveItem 在单个performBatchUpdates 中不能很好地发挥作用。根据视频中提出的解决方法,我们必须在单独的performBatchUpdates 中调用reloadItems。
我们也不遵循视频中提出的 100% 方法(先执行reloadItems 键入 performBatchUpdates,然后插入/移动/删除键入 performBatchUpdates)。
这是因为我们注意到即使对于简单的情况它也不能很好地工作。 一些奇怪的行为,包括reloadItems 会导致重复的单元格 UI 显示在屏幕上。我们发现的“几乎”工作方法是
- 为插入、移动和删除执行 performBatchUpdates
- 在 performBatchUpdates 的完成处理程序中,为 reloadItems 执行另一个 performBatchUpdates
NSFetchedResultsController + UICollectionView 集成
private var blockOperations: [BlockOperation] = []
// reloadItems and moveItem do not play well together. We are using the following workaround proposed at
// https://developer.apple.com/videos/play/wwdc2018/225/
private var blockUpdateOperations: [BlockOperation] = []
extension DashboardViewController: NSFetchedResultsControllerDelegate {
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
if type == NSFetchedResultsChangeType.insert {
print(">> insert")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.insertItems(at: [newIndexPath!])
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print(">> update")
blockUpdateOperations.append(
BlockOperation(block: { [weak self] in
if let self = self, let indexPath = indexPath {
self.collectionView.reloadItems(at: [indexPath])
}
})
)
}
else if type == NSFetchedResultsChangeType.move {
print(">> move")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self, let newIndexPath = newIndexPath, let indexPath = indexPath {
self.collectionView.moveItem(at: indexPath, to: newIndexPath)
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print(">> delete")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.deleteItems(at: [indexPath!])
}
})
)
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
if type == NSFetchedResultsChangeType.insert {
print(">> section insert")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.insertSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.update {
print(">> section update")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.reloadSections(IndexSet(integer: sectionIndex))
}
})
)
}
else if type == NSFetchedResultsChangeType.delete {
print(">> section delete")
blockOperations.append(
BlockOperation(block: { [weak self] in
if let self = self {
self.collectionView!.deleteSections(IndexSet(integer: sectionIndex))
}
})
)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
if blockOperations.isEmpty {
performBatchUpdatesForUpdateOperations()
} else {
collectionView.performBatchUpdates({ [weak self] () -> Void in
guard let self = self else { return }
for operation: BlockOperation in self.blockOperations {
operation.start()
}
self.blockOperations.removeAll(keepingCapacity: false)
}, completion: { [weak self] (finished) -> Void in
print("blockOperations completed")
guard let self = self else { return }
self.performBatchUpdatesForUpdateOperations()
})
}
}
private func performBatchUpdatesForUpdateOperations() {
if blockUpdateOperations.isEmpty {
return
}
collectionView.performBatchUpdates({ [weak self] () -> Void in
guard let self = self else { return }
for operation: BlockOperation in self.blockUpdateOperations {
operation.start()
}
self.blockUpdateOperations.removeAll(keepingCapacity: false)
}, completion: { [weak self] (finished) -> Void in
print("blockUpdateOperations completed")
guard let self = self else { return }
})
}
}
当不涉及“部分”操作时,上述方式“几乎”运行良好。
对于上面的动画,你会观察日志记录
>> move
blockOperations completed
>> move
blockOperations completed
>> move
blockOperations completed
但是,当添加/删除部分时,performBatchUpdates 的完成处理程序不会被调用!
对于上面的动画,你会观察日志记录
>> section delete
>> move
>> section insert
>> move
这意味着完成处理程序块没有被执行!有谁知道为什么会这样,我该如何解决这个问题?
我希望“blockOperations completed”应该被打印出来。预期的日志应该是
>> section delete
>> move
blockOperations completed
>> section insert
>> move
blockOperations completed
谢谢。
【问题讨论】:
-
日志记录与代码不匹配。我没有看到您在代码中打印“blockOperations completed”。我看到您打印“blockUpdateOperations completed”。真实的故事是什么?
-
对不起。解决了这个问题。
标签: ios swift uicollectionview nsfetchedresultscontroller