Update on July 8 2022 - Apple appears to have fixed the two finger scrolling bug, although the interaction is still a bit buggy.
Collection view + compositional layout + diffable data source + drag and drop does not seem to work together. This is on a completely vanilla example modeled after this (which works fine.)
Dragging an item with one finger works until you use a second finger to simultaneously scroll, at which point it crashes 100% of the time. I would love for this to be my problem and not an Apple oversight.
I tried using a flow layout and the bug disappears. Also it persists even if I don't use the list configuration of compositional layout, so that's not it.
Any ideas? Potential workarounds? Is this a known issue?
(The sample code below should run as-is on a blank project with a storyboard containing one view controller pointing to the view controller class.)
import UIKit
struct VideoGame: Hashable {
let id = UUID()
let name: String
}
extension VideoGame {
static var data = [VideoGame(name: "Mass Effect"),
VideoGame(name: "Mass Effect 2"),
VideoGame(name: "Mass Effect 3"),
VideoGame(name: "ME: Andromeda"),
VideoGame(name: "ME: Remaster")]
}
class CollectionViewDataSource: UICollectionViewDiffableDataSource<Int, VideoGame> {
// 1
override func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
return true
}
override func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
guard let fromGame = itemIdentifier(for: sourceIndexPath),
sourceIndexPath != destinationIndexPath else { return }
var snap = snapshot()
snap.deleteItems([fromGame])
if let toGame = itemIdentifier(for: destinationIndexPath) {
let isAfter = destinationIndexPath.row > sourceIndexPath.row
if isAfter {
snap.insertItems([fromGame], afterItem: toGame)
} else {
snap.insertItems([fromGame], beforeItem: toGame)
}
} else {
snap.appendItems([fromGame], toSection: sourceIndexPath.section)
}
apply(snap, animatingDifferences: false)
}
}
class DragDropCollectionViewController: UIViewController {
var videogames: [VideoGame] = VideoGame.data
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewCompositionalLayout.list(using: UICollectionLayoutListConfiguration(appearance: .insetGrouped)))
lazy var dataSource: CollectionViewDataSource = {
let dataSource = CollectionViewDataSource(collectionView: collectionView, cellProvider: { (collectionView, indexPath, model) -> UICollectionViewListCell in
return collectionView.dequeueConfiguredReusableCell(using: self.cellRegistration, for: indexPath, item: model)
})
return dataSource
}()
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, VideoGame> { (cell, indexPath, model) in
var configuration = cell.defaultContentConfiguration()
configuration.text = model.name
cell.contentConfiguration = configuration
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.frame = view.bounds
collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
collectionView.dragDelegate = self
collectionView.dropDelegate = self
collectionView.dragInteractionEnabled = true
var snapshot = dataSource.snapshot()
snapshot.appendSections([0])
snapshot.appendItems(videogames, toSection: 0)
dataSource.applySnapshotUsingReloadData(snapshot)
}
}
extension DragDropCollectionViewController: UICollectionViewDragDelegate {
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
guard let item = dataSource.itemIdentifier(for: indexPath) else {
return []
}
let itemProvider = NSItemProvider(object: item.id.uuidString as NSString)
let dragItem = UIDragItem(itemProvider: itemProvider)
dragItem.localObject = item
return [dragItem]
}
}
// 4
extension DragDropCollectionViewController: UICollectionViewDropDelegate {
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
//Not needed
}
}
If you download the modern Collectionviews project from Apple, there is one that shows compositional layout, diffable datasource and reordering. However this is only for their new list cells, not a reg CollectionView cell.
You can find it here: Modern CollectionViews
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.