简体   繁体   English

使用 UICollectionViewDiffableDataSource 和 NSFetchedResultsController 重新排序单元格

[英]Reordering Cells with UICollectionViewDiffableDataSource and NSFetchedResultsController

I'm using a UICollectionViewDiffableDataSource and a NSFetchedResultsController to populate my UICollectionView inside my UIViewController .我正在使用UICollectionViewDiffableDataSourceUICollectionViewNSFetchedResultsController中填充我的UIViewController

To add the ability of reordering cells I added a UILongPressGestureRecognizer and subclassed UICollectionViewDiffableDataSource in order to use it's canMoveItemAt: and moveItemAt: methods.为了添加重新排序单元格的功能,我添加了一个UILongPressGestureRecognizer和子类UICollectionViewDiffableDataSource以便使用它的canMoveItemAt:moveItemAt:方法。

When reordering a cell the following things happen:重新排序单元格时会发生以下情况:

  1. moveItemAt: is called and I update the objects position property and save the MOC moveItemAt:被调用,我更新对象 position 属性并保存 MOC
  2. controllerDidChangeContent: of the NSFetchedResultsControllerDelegate is called and I create a new snapshot from the current fetchedObjects and apply it.调用NSFetchedResultsControllerDelegatecontrollerDidChangeContent:并且我从当前 fetchedObjects 创建一个新快照并应用它。

When I apply dataSource?.apply(snapshot, animatingDifferences: true) the cells switch positions back immediately.当我应用dataSource?.apply(snapshot, animatingDifferences: true)时,单元格会立即切换回位置。 If I set animatingDifferences: false it works, but all cells are reloaded visibly .如果我设置animatingDifferences: false它可以工作,但是所有单元格都被重新加载了。

Is there any best practice here, how to implement cell reordering on a UICollectionViewDiffableDataSource and a NSFetchedResultsController ?这里有什么最佳实践,如何在UICollectionViewDiffableDataSourceNSFetchedResultsController上实现单元格重新排序?

Here are my mentioned methods:以下是我提到的方法:

// ViewController 
func createSnapshot(animated: Bool = true) {
    var snapshot = NSDiffableDataSourceSnapshot<Int, Favorite>()
    snapshot.appendSections([0])
    snapshot.appendItems(provider.fetchedResultsController.fetchedObjects ?? [])
    dataSource?.apply(snapshot, animatingDifferences: animated)
}

// NSFetchedResultsControllerDelegate
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    createSnapshot(animated: false)
}

// Subclassed UICollectionViewDiffableDataSource
override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
    provider.moveFavorite(from: sourceIndexPath.row, to: destinationIndexPath.row)
}

// Actual cell moving in a provider class
public func moveFavorite(from source: Int, to destination: Int) {
    guard let favorites = fetchedResultsController.fetchedObjects else { return }
    if source < destination {
        let partialObjects = favorites.filter({ $0.position <= destination && $0.position >= source })

        for object in partialObjects {
            object.position -= 1
        }

        let movedFavorite = partialObjects.first
        movedFavorite?.position = Int64(destination)
    }
    else {
        let partialObjects = favorites.filter({ $0.position >= destination && $0.position <= source })

        for object in partialObjects {
            object.position += 1
        }

        let movedFavorite = partialObjects.last
        movedFavorite?.position = Int64(destination)
    }
    do {
        try coreDataHandler.mainContext.save()
    } catch let error as NSError {
        print(error.localizedDescription)
    }
}

My solution to the same issue is to subclass the UICollectionViewDiffableDataSource and implement the canMoveItemAt method in the subclass to answer true.我对同一问题的解决方案是将 UICollectionViewDiffableDataSource 子类化并在子类中实现 canMoveItemAt 方法以回答 true。 The animation seems to work fine for me if the longPressAction case of.ended does three things: animation 对我来说似乎工作正常,如果 longPressAction 案例 of.ended 做了三件事:

  1. update the model更新 model

  2. call dateSource.collectionView(..moveItemAt:..)调用 dateSource.collectionView(..moveItemAt:..)

  3. run your dataSource.apply运行你的 dataSource.apply

The other usual methods for drag behavior have to be also implemented which it looks like you have done.拖拽行为的其他常用方法也必须实现,看起来你已经完成了。 FYI for others- These methods are well documented in the section for 'Reordering Items Interactively' of UICollectionView.仅供参考 - 这些方法在 UICollectionView 的“交互式重新排序项目”部分中有详细记录。 https://developer.apple.com/documentation/uikit/uicollectionview https://developer.apple.com/documentation/uikit/uicollectionview

class PGLDiffableDataSource: UICollectionViewDiffableDataSource<Int, Int> {
    override func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
        return true
    }

}

No need to subclass.无需子类。 Starting in iOS 14.0, UICollectionViewDiffableDataSource supports reordering handlers you can implement.从 iOS 14.0 开始,UICollectionViewDiffableDataSource 支持您可以实现的重新排序处理程序

let data_source = UICollectionViewDiffableDataSource<MySection, MyModelObject>( collectionView: collection_view, cellProvider:
{
    [weak self] (collection_view, index_path, video) -> UICollectionViewCell? in
        
    let cell = collection_view.dequeueReusableCell( withReuseIdentifier: "cell", for: index_path ) as! MyCollectionViewCell

    if let self = self
    {
        //setModel() is my own method to update the view in MyCollectionViewCell
        cell.setModel( self.my_model_objects[index_path.item] )
    }

    return cell
})

// Allow every item to be reordered as long as there's 2 or more
diffable_data_source.reorderingHandlers.canReorderItem = 
{ 
    item in 
    my_model_objects.count >= 2 return true 
}

//Update your model objects before the reorder occurs. 
//You can also use didReorder, but it might be useful to have your
//model objects in the correct order before dequeueReusableCell() is 
//called so you can update the cell's view with the correct model object.
diffable_data_source.reorderingHandlers.willReorder = 
{ 
    [weak self] transaction in
    
    guard let self = self else { return }
    
    self.my_model_objects = transaction.finalSnapshot.itemIdentifiers
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM