NSFetchResultController не обновляет ячейку для CollectionView - PullRequest
1 голос
/ 24 февраля 2020

Я пытаюсь использовать 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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...