我正在尝试用UICollectionViewDropDelegate (在iOS 13中引入)来实现iOS。
UICollectionViewDragDelegate,它工作得很好。实现func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) (您甚至可以在方法为空的情况下尝试)。
该应用程序将构建并运行,但一旦您开始拖动一个单元格,应用程序就会立即崩溃,并收到以下消息:
终止应用程序由于非常规异常'NSInternalInconsistencyException',原因:'UICollectionView必须通过UICollectionViewDiffableDataSource API更新时充当UICollectionView的dataSource:请不要在UICollectionView上直接调用突变API。
因此,这个方法似乎是试图直接修改UICollectionView (可能是在移动项目时移动单元格?)我想不出该怎么绕过这种行为。
有什么想法?
发布于 2020-03-13 16:42:17
这是一个相当容易解决的问题。从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
}
}然后我们有了函数reorderItems(),我们需要处理collectionView中的实际更改。需要明确的一点是,快照(NSDiffableDataSourceSnapshot<>)和dataSource (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)
}
}发布于 2020-03-19 23:41:13
如果您正在使用collectionView.performBatchUpdates()进行更改以更新您的collectionView,这将导致崩溃,似乎您需要对数据源(UICollectionViewDiffableDataSource)上的快照进行操作,您可以在错误上看到:"UICollectionViewDiffableDataSource API时充当UICollectionView的dataSource:请不要直接调用UICollectionView上的突变API“
试试这个:
// 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)
}
}其中,您的条目是一个结构:
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是:
private func reloadSnapshot(animated: Bool) {
var snapshot = NSDiffableDataSourceSnapshot<Int, UIImage>()
snapshot.appendSections([0])
snapshot.appendItems(entry?.images ?? [])
dataSource?.apply(snapshot, animatingDifferences: animated)
}最后在您的UICollectionViewDropDelegate上:
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的“催化剂由教程”
发布于 2020-04-06 07:18:12
还有一件事:
如果您使用UICollectionViewDropDelegate方法返回一个UICollectionViewDropProposal,collectionView(_:dropSessionDidUpdate:withDestinationIndexPath destinationIndexPath:),则该方法在动画化拖放时将调用遮罩下的不可扩散方法。
我猜苹果从来没有用不同的数据源对此进行过测试。您将需要删除此方法,并以不同的方式实现它的动画。
https://stackoverflow.com/questions/59699679
复制相似问题