首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >UICollectionViewDropDelegate和DiffableDataSource

UICollectionViewDropDelegate和DiffableDataSource
EN

Stack Overflow用户
提问于 2020-01-11 23:40:41
回答 3查看 3K关注 0票数 6

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

  • I已经实现了UICollectionViewDragDelegate,它工作得很好。
  • ,我设置了

实现func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) (您甚至可以在方法为空的情况下尝试)。

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

终止应用程序由于非常规异常'NSInternalInconsistencyException',原因:'UICollectionView必须通过UICollectionViewDiffableDataSource API更新时充当UICollectionView的dataSource:请不要在UICollectionView上直接调用突变API。

因此,这个方法似乎是试图直接修改UICollectionView (可能是在移动项目时移动单元格?)我想不出该怎么绕过这种行为。

有什么想法?

EN

回答 3

Stack Overflow用户

发布于 2020-03-13 16:42:17

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

代码语言:javascript
复制
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
   }
}

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

代码语言:javascript
复制
/// 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)
   }
}
票数 5
EN

Stack Overflow用户

发布于 2020-03-19 23:41:13

如果您正在使用collectionView.performBatchUpdates()进行更改以更新您的collectionView,这将导致崩溃,似乎您需要对数据源(UICollectionViewDiffableDataSource)上的快照进行操作,您可以在错误上看到:"UICollectionViewDiffableDataSource API时充当UICollectionView的dataSource:请不要直接调用UICollectionView上的突变API“

试试这个:

代码语言:javascript
复制
// MARK: - Properties
  var dataSource: UICollectionViewDiffableDataSource<Int, UIImage>?

  var entry: Entry? {
    didSet {
      guard let entry = entry else { return }
      let dateFormatter = DateFormatter()
      dateFormatter.setLocalizedDateFormatFromTemplate("MMM dd yyyy, hh:mm")
      title = dateFormatter.string(from: entry.dateCreated)
    }
  }

其中,您的条目是一个结构:

代码语言:javascript
复制
struct Entry {
  let id = UUID().uuidString
  let dateCreated = Date()
  var log: String?
  var images: [UIImage] = []
  var isFavorite: Bool = false
}

extension Entry: Hashable {
  func hash(into hasher: inout Hasher) {
    hasher.combine(dateCreated)
    hasher.combine(log)
  }

  static func == (lhs: Entry, rhs: Entry) -> Bool {
    return lhs.dateCreated == rhs.dateCreated &&
      lhs.log ?? "" == rhs.log ?? "" &&
      lhs.images == rhs.images &&
      lhs.isFavorite == rhs.isFavorite
  }
}

reloadSnapshot是:

代码语言:javascript
复制
private func reloadSnapshot(animated: Bool) {
    var snapshot = NSDiffableDataSourceSnapshot<Int, UIImage>()
    snapshot.appendSections([0])
    snapshot.appendItems(entry?.images ?? [])
    dataSource?.apply(snapshot, animatingDifferences: animated)
  }

最后在您的UICollectionViewDropDelegate上:

代码语言:javascript
复制
func collectionView( _ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {

let destinationIndex = coordinator.destinationIndexPath?.item ?? 0

for item in coordinator.items {
  if coordinator.session.localDragSession != nil,
    let sourceIndex = item.sourceIndexPath?.item {

    self.entry?.images.remove(at: sourceIndex)
  }

  item.dragItem.itemProvider.loadObject(ofClass: UIImage.self) {
    (object, error) in
    guard let image = object as? UIImage, error == nil else {
      print(error ?? "Error: object is not UIImage")
      return
    }
    DispatchQueue.main.async {
      self.entry?.images.insert(image, at: destinationIndex)
      self.reloadSnapshot(animated: true)
    }
  }
}
}

我所有这些都是基于阅读和工作从raywenderlich.com的“催化剂由教程”

票数 0
EN

Stack Overflow用户

发布于 2020-04-06 07:18:12

还有一件事:

如果您使用UICollectionViewDropDelegate方法返回一个UICollectionViewDropProposalcollectionView(_:dropSessionDidUpdate:withDestinationIndexPath destinationIndexPath:),则该方法在动画化拖放时将调用遮罩下的不可扩散方法。

我猜苹果从来没有用不同的数据源对此进行过测试。您将需要删除此方法,并以不同的方式实现它的动画。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59699679

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档