LoginSignup
4
3

More than 3 years have passed since last update.

UICollectionViewのドラッグで、元の位置に戻すときに一瞬ちらつく現象

Posted at

ドラッグ時にちらつく

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を設定すれば実装できる。
ドラッグ中のスタイルの設定はまた別でデリゲートがあります。

※このコードは動作確認してません。

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3