0

I am making a multiple selection feature for my collection view which shows photos from the user's library. I keep track of the selected indexPaths in an array and I want to update them in case a photo library change observer event happens in the middle of selecting cells. for example, if a user has selected indexes 3 and 4 and a change observer event removes indexes 1 and 2 from the collection view, selected indexes should change to 1 and 2.

I am trying to do it manually using these functions:

fileprivate func removeIndicesFromSelections(indicesToRemove:IndexSet){


        var itemToRemove: Int?

        for (_, removeableIndex) in indicesToRemove.map({$0}).enumerated() {

            itemToRemove = nil

            for (itemIndex,indexPath) in selectedIndices.enumerated() {

                //deduct 1 from indices after the deletion index
                if (indexPath.item > removeableIndex) && (indexPath.item > 0) {

                    selectedIndices[itemIndex] = IndexPath(item: indexPath.item - 1, section: 0)

                } else if indexPath.item == removeableIndex {

                    itemToRemove = itemIndex

                }
            }

            if let remove = itemToRemove {
                selectedIndices.remove(at: remove)
                disableDeleteButtonIfNeeded()
            }

        }


    }


fileprivate func moveSelectedIndicesAfterInsertion (insertedIndices:IndexSet){

    for (_, insertedIndex) in insertedIndices.map({$0}).enumerated() {

        for (itemIndex,indexPath) in selectedIndices.enumerated() {

            //add 1 to indices after the insertion index
            if (indexPath.item >= insertedIndex) {

                selectedIndices[itemIndex] = IndexPath(item: indexPath.item + 1, section: 0)

            }


        }

    }

}

However, these are getting more complicated than I expected and I keep finding bugs in them. Is there any better way to handle this situation (such as any built in collection view capabilities) or I just have to come up with my own functions like above?

HemOdd
  • 697
  • 1
  • 7
  • 23

2 Answers2

0

You're on the right path, but you should store a reference to what object the user actually selected, not where they selected it (since that can change).

In this case, you should keep a reference to the selected photos' identifiers (see docs) and then you can determine what cell/index-path should be selected. You can compare your selection array against your image datasource to determine what the most up-to-date index path is.

Hobbes the Tige
  • 3,753
  • 2
  • 22
  • 21
0

There is a solution provided by Apple. You can find more information in official documentation page:

Bacically you want to adopt PHPhotoLibraryChangeObserver and implement the following function:

func photoLibraryDidChange(_ changeInstance: PHChange) {
    guard let collectionView = self.collectionView else { return }
    // Change notifications may be made on a background queue.
    // Re-dispatch to the main queue to update the UI.
    DispatchQueue.main.sync {
        // Check for changes to the displayed album itself
        // (its existence and metadata, not its member assets).
        if let albumChanges = changeInstance.changeDetails(for: assetCollection) {
            // Fetch the new album and update the UI accordingly.
            assetCollection = albumChanges.objectAfterChanges! as! PHAssetCollection
            navigationController?.navigationItem.title = assetCollection.localizedTitle
        }
        // Check for changes to the list of assets (insertions, deletions, moves, or updates).
        if let changes = changeInstance.changeDetails(for: fetchResult) {
            // Keep the new fetch result for future use.
            fetchResult = changes.fetchResultAfterChanges
            if changes.hasIncrementalChanges {
                // If there are incremental diffs, animate them in the collection view.
                collectionView.performBatchUpdates({
                    // For indexes to make sense, updates must be in this order:
                    // delete, insert, reload, move
                    if let removed = changes.removedIndexes where removed.count > 0 {
                        collectionView.deleteItems(at: removed.map { IndexPath(item: $0, section:0) })
                    }
                    if let inserted = changes.insertedIndexes where inserted.count > 0 {
                        collectionView.insertItems(at: inserted.map { IndexPath(item: $0, section:0) })
                    }
                    if let changed = changes.changedIndexes where changed.count > 0 {
                        collectionView.reloadItems(at: changed.map { IndexPath(item: $0, section:0) })
                    }
                    changes.enumerateMoves { fromIndex, toIndex in
                        collectionView.moveItem(at: IndexPath(item: fromIndex, section: 0),
                                                to: IndexPath(item: toIndex, section: 0))
                    }
                })
            } else {
                // Reload the collection view if incremental diffs are not available.
                collectionView.reloadData()
            }
        }
    }
}
fewlinesofcode
  • 3,007
  • 1
  • 13
  • 30