##ドラッグ時にちらつく
Xcode9時代にUICollectionViewのドラッグアンドドロップによる並び替え機能を実装していた画面にて、Xcode11.3でビルドしたところ、一見問題なく動いているものの、
- ドラッグ開始
- 元の位置に移動する
- 瞬時に元の位置に戻ったり、ドラッグ位置に戻ったりとちらつく
という現象に遭遇しました。
実はというと、Autolayoutで警告メッセージが表示されていたので、多分そっちを解決すべきなんですが、どうしてもAutolayoutが解決しなかった。
で、iOS11以降から使用できるというUICollectionViewDragDelegate/DropDelegateがあるためこちらに書き換えたところ、現象が解消されました。
UICollectionViewDragDelegate
ドラッグ開始のデリゲート。
UIDragItemのitemProviderを通じてドラッグ後イベントにパラメータを渡したりできる。
よくある書き方はこんなかんじ。
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let index = indexPath.row.description
let itemProvider = NSItemProvider(object: index as NSString)
let dragItem = UIDragItem(itemProvider: itemProvider)
return [dragItem]
}
並び替え処理の場合、このデリゲート内で
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal{
return UICollectionViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
}
と返してあげる。
(ホントわかりにくい)
ドラッグ後のデリゲートはこちら。
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
// 遷移先のindexPathはこうして取得
let destinationIndexPath: IndexPath = coordinator.destinationIndexPath
switch coordinator.proposal.operation {
case .move:
// UIDragItemは複数ある
let items = coordinator.items
// 先頭の1要素目から遷移元indexPathの取得はこういう感じで
let firstIndexPath = items.first!.sourceIndexPath
// performBatchUpdatesの中で、データの更新とCollectionViewのセルの増減操作をする。
collectionView.performBatchUpdates({
// データソースの更新
let n = datalist.remove(at: sourceIndexPath.item)
datalist.insert(n, at: destinationIndexPath.item)
//セルの移動
collectionView.deleteItems(at: [sourceIndexPath])
collectionView.insertItems(at: [destinationIndexPath])
})
// dropを呼ぶと、指定したindexPathの位置にCellがスッと入る動きをしてくれる
coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
default:
return
}
}
}
という感じ。
あとはUICollectionViewのDrag/DropDelegateを設定すれば実装できる。
ドラッグ中のスタイルの設定はまた別でデリゲートがあります。
※このコードは動作確認してません。