swift - NSFetchResultController 没有更新 CollectionView 的单元格
问题描述
我正在尝试将 NSFetchController 与 CollectionView 一起使用。我的问题是当我更改 NSManagedObject 的属性值时,我的单元格没有正确更新。这是一个链接,可以更好地展示我在说什么。好的,所以如果您观看视频,您基本上可以看到该应用程序的功能。它有一堆英雄,当您单击一个单元格时,它会转到另一个视图控制器并显示图片、名称和发布者。现在你也能看到问题了,当我把名字改成“Amanda Doe”并保存时。它一开始就插入到了正确的位置,但单元格标签没有更新。
当我再次单击单元格时,它会在导航标题上显示 Amanda Doe,但单元格标签仍然相同。为了显示名称,我必须重新启动应用程序或单击同一单元格,然后再次单击保存。
这就是我的 NSFetchResultController 的样子。我按发布者名称排序,然后按英雄名称排序。它还具有基于发布者的多个部分。
private var blockOperations: [BlockOperation] = []
lazy var heroController: NSFetchedResultsController<Hero> = {
let request: NSFetchRequest<Hero> = Hero.fetchRequest()
let nameSort = NSSortDescriptor(key: "name", ascending: true)
let publisherSort = NSSortDescriptor(key: "publisher.name", ascending: true)
request.sortDescriptors = [publisherSort, nameSort]
let context = CoreDataManager.shared.persistentContainer.viewContext
let controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: "publisher.name", cacheName: nil)
controller.delegate = self
do {
try controller.performFetch()
} catch let err {
print("Unable to fetch heros from controller.", err)
}
return controller
}()
这里的代码基本上是我发现大多数人用来让 NSFetchResultController 与 CollectionView 一起工作的样板代码。
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
blockOperations.removeAll(keepingCapacity: false)
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
let op: BlockOperation
switch type {
case .delete:
op = BlockOperation { self.collectionView.deleteSections(IndexSet(integer: sectionIndex))}
blockOperations.append(op)
case .insert:
op = BlockOperation { self.collectionView.insertSections(IndexSet(integer: sectionIndex))}
blockOperations.append(op)
case .move:
break
case .update:
op = BlockOperation { self.collectionView.reloadSections(IndexSet(integer: sectionIndex))}
blockOperations.append(op)
@unknown default:
fatalError()
}
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType,newIndexPath: IndexPath?) {
let op: BlockOperation
switch type {
case .insert:
guard let newIndexPath = newIndexPath else { return }
op = BlockOperation { self.collectionView.insertItems(at: [newIndexPath]) }
case .delete:
guard let indexPath = indexPath else { return }
op = BlockOperation { self.collectionView.deleteItems(at: [indexPath]) }
case .move:
guard let indexPath = indexPath, let newIndexPath = newIndexPath else { return }
op = BlockOperation { self.collectionView.moveItem(at: indexPath, to: newIndexPath) }
case .update:
guard let indexPath = indexPath else { return }
op = BlockOperation { self.collectionView.reloadItems(at: [indexPath]) }
@unknown default:
fatalError()
}
blockOperations.append(op)
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
collectionView.performBatchUpdates({
self.blockOperations.forEach { $0.start() }
}, completion: { finished in
self.blockOperations.removeAll(keepingCapacity: false)
})
}
所以我错过了什么,是因为我错过了该部分的 .move 案例吗?我正在玩这个应用程序,发现单元格是否不移动或必须移动到新的索引路径,它工作正常。这是为什么?非常感谢任何帮助。谢谢。
编辑 1
在玩了更多代码之后,我遇到了另一个问题。问题 1 与您在视频中看到的一样。单元格没有正确更新,我不知道为什么。我尝试按照@pbasdf 的建议对其进行调试并发现了这一点。首先,当我将名称更改为“Amanda Doe”时,它会触发didChangeAnObject: .move两次,但不会触发didChangeAnObject: .update。当我再次单击同一个单元格并将名称更改为“Amanda Doee”时,它会调用didChangeAnObject: .update并使用正确的名称更新单元格。所以基本上如果调用了didChangeAnObject: .move案例,则单元格不会更新,并且如果调用了didChangeAnObject: .update案例,它会使用正确的名称更新单元格。
问题 2 是当我更改发布者名称时。所以现在的名字是“Amanda Doee”,出版商的名字是“ABC Studios”。现在,如果我只是将发布者名称更改为“ABC Studioss”,它应该将单元格移动到一个名为“ABC Studioss”的新部分,因为我的 NSFetchResultController。问题是它不会保持在相同的位置,而是调用didChangeAnObject: .update案例。
现在我找到了一种可行的方法。为了让它工作,我需要更改名称和发布者。如果我只是更改发布者,它将不起作用。所以我不得不把“Amanda Doee”改成“Amanda Doeee”,把“ABC Studios”改成“ABC Studioss”。这就是我更改名称和发布者并保存它时调用方法的方式。didChangeSectionInfo:.insert,didChangeAnObject:.move,didChangeSectionInfo:.insert,didChangeAnObject:.move。现在,如果我只是更改发布者的名称,这只会被称为didChangeAnObject: .update。我将不得不重新启动我的应用程序以查看带有“Amanda Doee”的名为“ABC Studioss”的新部分的更改。
所以我真的不知道发生了什么以及为什么会这样。
编辑 2
这是我在其他 VC 中用于保存编辑或创建新英雄的代码。
@objc private func handleSave() {
let context = CoreDataManager.shared.persistentContainer.viewContext
if let hero = hero {
hero.name = nameTF.text ?? ""
hero.publisher?.name = publisherTF.text
do {
try context.save()
}
catch let err {
print("Unable to save hero edits.", err)
}
}
else {
print("Create new hero.")
let hero = Hero(context: context)
hero.name = nameTF.text
let publisher = Publisher(context: context)
publisher.name = publisherTF.text
hero.publisher = publisher
guard let imageData = heroImageView.image?.jpegData(compressionQuality: 1) else {
try? context.save()
return
}
let photo = Photo(context: context)
photo.data = imageData
hero.photo = photo
try? context.save()
}
navigationController?.popViewController(animated: true)
}
解决方案
推荐阅读
- jenkins - Windows 上的詹金斯 GUI 测试
- php - 如何在magento 1.9中将标题块调用到另一个html或php文件
- javascript - 擦除字符会产生错误
- ios - 对成员“过滤器”swift4 的模糊引用
- swift - 在 Swift 中从 Array 的字典中过滤项目
- php - 没有实体的存储库
- javascript - 如何将 React 方法添加到 React 之外的对象
- postgresql - Postgres & JSONB - Levenshtein 参数
- django - 使用 modelname.filevariable.url 上传文件的 Django 电子邮件附件
- javascript - 如果元素不是直接子元素,则仅使用 CSS 选择具有特定类的元素的第一次出现