Я пытаюсь использовать NSFetchController с CollectionView. Моя проблема в том, что когда я изменяю значение свойства для моего NSManagedObject, мои ячейки не обновляются правильно. Вот ссылка , чтобы лучше показать, о чем я говорю. Итак, если вы посмотрите видео, вы сможете увидеть, в основном, что делает приложение. У него куча героев, и когда вы щелкаете по ячейке, он переходит к другому контроллеру представления и показывает изображение, имя и издателя. Теперь вы также смогли увидеть проблему, когда я тоже сменил имя «Аманда Доу» и сохранил его. В начале он был вставлен в нужное место, но метка ячейки не была обновлена.
Когда я снова щелкаю по ячейке, в заголовке навигации отображается Аманда Доу, но метка ячейки остается прежней. Чтобы имя отображалось, я должен перезапустить приложение или щелкнуть ту же ячейку и просто нажать «Сохранить» еще раз.
Так выглядит мой 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, и обнаружил это. Сначала, когда я меняю имя на «Аманда Доу», дважды запускается регистр didChangeAnObject: .move , но никогда didChangeAnObject: .update . Когда я снова щелкаю по той же ячейке и меняю имя на «Аманда Дои», она вызывает didChangeAnObject: .update и обновляет ячейку с правильным именем. Таким образом, в принципе, если вызывается didChangeAnObject: .move , ячейка не обновляется, а если вызывается didChangeAnObject: .update , она обновляет ячейку с правильным именем.
Проблема 2 - когда я меняю имя издателя. На данный момент имя «Аманда Дои», а имя издателя «AB C Stud ios». Теперь, если я просто изменил имя издателя на «AB C Studioss», он должен переместить ячейку в новый раздел под названием «AB C Studioss» из-за моего NSFetchResultController. Дело не в том, что он остается в той же позиции и вызывает didChangeAnObject: .update case.
Теперь я нашел способ, которым это работает. Для того, чтобы он работал, мне нужно изменить имя и издателя. Это не сработает, если я просто поменяю издателя. Поэтому мне пришлось бы поменять «Аманда Дои» на «Аманда Дои» и «AB C Stud ios» на «AB C Studioss». Так меняются методы, когда я меняю имя и издателя и сохраняю его. didChangeSectionInfo: .insert , didChangeAnObject: .move , didChangeSectionInfo: .insert , didChangeAnObject: .move . Теперь, если бы я просто изменил имя издателя, он получил бы только имя didChangeAnObject: .update . Я должен был бы перезапустить свое приложение, чтобы увидеть изменения в новом разделе под названием «AB C Studioss» с «Amanda Doee».
Так что я действительно не знаю, что происходит и почему это так.
Edit 2
Вот код, который я использую в другом V C для сохранения правок или создания нового героя.
@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)
}