UICollectionViewDropDelegate 和 DiffableDataSource

Kev*_*ark 6 datasource ios swift

我正在尝试UICollectionViewDropDelegate使用 DiffableDataSource (在 iOS 13 中引入)来实现。

  • 我已经实现了UICollectionViewDragDelegate,效果很好。
  • 我设置collectionView.dropDelegate = self
  • 我实现了func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)(你甚至可以在方法为空的情况下尝试)。

该应用程序将构建并运行,但一旦您开始拖动单元格,该应用程序就会立即崩溃并显示以下消息:

由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“当充当 UICollectionView 的数据源时,UICollectionView 必须通过 UICollectionViewDiffableDataSource API 进行更新:请不要直接在 UICollectionView 上调用突变 API。

所以看起来这个方法正在尝试直接修改 UICollectionView (也许在移动项目时移动单元格?)我不知道如何绕过这种行为。

想法?

Cos*_*tar 5

这是一个相当简单的解决方案。从UICollectionViewDropDelegate函数performDropWith开始。请注意,我只需要一个 .move 而不需要 .copy,但您只需添加一个 copyItems() 方法,类似于下面的 reorderItems() 方法:

func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
   var destinationIndexPath: IndexPath
   if let indexPath = coordinator.destinationIndexPath {
      destinationIndexPath = indexPath
   } else {
      let section = collectionView.numberOfSections - 1
      let row = collectionView.numberOfItems(inSection: section)
      destinationIndexPath = IndexPath(row: row, section: section)
   }
   switch coordinator.proposal.operation {
   case .move:
      self.reorderItems(coordinator: coordinator, destinationIndexPath: destinationIndexPath, collectionView: collectionView)
      break
   case .copy:
      // Not copying between collections so this block not needed.
      return
   default:
      return
   }
}
Run Code Online (Sandbox Code Playgroud)

然后我们需要函数 reorderItems() 来处理 collectionView 中的实际更改。需要明确的一件事是,快照 (NSDiffableDataSourceSnapshot<>) 和数据源 (UICollectionViewDiffableDataSource<>) 都是类变量。

/// reorderItems method
/// This method moves a cell from the sourceIndexPath to the destinationIndexPath within the same UICollectionView
///
/// - Parameters:
///   - coordinator: UICollectionViewDropCoordinator obtained in performDropWith
///   - destinationIndexPath: IndexPath where user dropped the element.
///   - collectionView: UICollectionView object where reordering is done.
private func reorderItems(coordinator: UICollectionViewDropCoordinator, destinationIndexPath: IndexPath, collectionView: UICollectionView) {
   let items = coordinator.items
   if items.count == 1, let item = items.first, let sourceIndexPath = item.sourceIndexPath {
      var destIndexPath = destinationIndexPath
      if destIndexPath.row >= collectionView.numberOfItems(inSection: destIndexPath.section) {
         destIndexPath.row = collectionView.numberOfItems(inSection: destIndexPath.section) - 1
      }

      /// Since my collectionView data is attributed to a Firebase.storage set of data, this is where I write my changes back to the store.

      snapshot.moveItem(dataSource.itemIdentifier(for: sourceIndexPath)!, beforeItem: dataSource.itemIdentifier(for: destinationIndexPath)!)
      dataSource.apply(snapshot, animatingDifference: true)
      coordinator.drop(items.first!.dragItem, toItemAt: destIndexPath)
   }
}
Run Code Online (Sandbox Code Playgroud)