0

Currently, I am having the following CoreData based NSFetchedResultsController.

I am using the following code snippet, to update UICollectionView's cell content.

This is how I update UICollectionView's cell content

// reloadItems and moveItem do not play well together. We are using the following workaround proposed at
// https://developer.apple.com/videos/play/wwdc2018/225/

extension BackupViewController: NSFetchedResultsControllerDelegate {
    func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
        if type == NSFetchedResultsChangeType.insert {
            blockOperations.append(
                BlockOperation(block: { [weak self] in
                    if let self = self {
                        self.collectionView!.insertItems(at: [newIndexPath!])
                    }
                })
            )
        }
        else if type == NSFetchedResultsChangeType.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 {
            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 {
            blockOperations.append(
                BlockOperation(block: { [weak self] in
                    if let self = self {
                        self.collectionView!.deleteItems(at: [indexPath!])
                    }
                })
            )
        }
    }

    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        if blockOperations.isEmpty {
            performBatchUpdatesForUpdateOperations()
        } else {
            let completion: ((Bool) -> Void) = { [weak self] (finished) -> Void in
                guard let self = self else { return }
                
                self.performBatchUpdatesForUpdateOperations()
            }
            
            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: completion)
        }
    }
    
    private func performBatchUpdatesForUpdateOperations() {
        if blockUpdateOperations.isEmpty {
            // TODO: Update top header. But how?
            
            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
            guard let self = self else { return }
            
            // TODO: Update top header. But how?
        })
    }
}

I would like to update the top header as well, after I have update the cell content.

May I know how I can achieve so?

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

1 Answers1

0

Your question doesn't seem to relate to Core Data. If I get you right, you just need a way to get a header for the corresponding to an index path section. This method can be used:

// Any place, where you have your index path.
let header = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionHeader, 
                                              at: indexPath)
// Update header any way you like.

For example:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
                didChange anObject: Any,
                at indexPath: IndexPath?,
                for type: NSFetchedResultsChangeType,
                newIndexPath: IndexPath?) {

  switch type {
    case .insert:
      // Insert a cell to the index path.
    case .delete:
      // Delete the cell at the index path.
    case .move:
      // Move the cell at the index path to the new index path.
    case .update:
      // Update the cell at the index path.

      // Update your header.
      let header = collectionView.supplementaryView(forElementKind: UICollectionView.elementKindSectionHeader,
                                                    at: indexPath)
      header.text = "Updated" // Or whatever.
  }
}

(This question and this also might be helpful too.)

lazarevzubov
  • 1,767
  • 2
  • 14
  • 24
  • It will be very wrong, to call `collectionView.supplementaryView` directly without going through `collectionView(viewForSupplementaryElementOfKind)`. We will end up with duplicated header view. – Cheok Yan Cheng Feb 18 '22 at 04:00
  • I didn't say you should. My answer implies that your collection view has a header (judging from your question, it does). The method I pointed out is not used for adding a header, but for obtaining a reference to an existing one to modify it. – lazarevzubov Feb 18 '22 at 07:30