カテゴリー: iOS

CollectionViewの並び替えを実装する

はじめに

こんにちは。Yossyです。
前回まではCollectionViewのカスタムレイアウトを作成していました。
このCollectionViewのセルの並び替え処理を実装をしたので、備忘録ついでに紹介したいと思います。

実装方法

自分が作成した時は要件の都合上、CollectionViewからセル(カスタムクラス実装)自体を取得し、セルが持つプロパティ(並び順含む)をDBに保存していました。そして、次にCollectionViewを表示する際にDBから並び順を含めたプロパティを読み出して使用するという流れです。

手順

①ロングタップの実装
②並び替え可とする実装
③並び替え時のデータ処理

ロングタップの実装

ロングタップジェスチャーを追加します。
ロングタップでセルを並び替えて、指を離すと並び替えが完了します。
移動するセルのインスタンスをプロパティに入れて保持しておきます。

   override func viewDidLoad() {
        super.viewDidLoad()
     // ロングタップジェスチャーを付与
        addLongTapGesture()
    }

  // 長押しでCollectionViewのセル移動処理を行う
    func addLongTapGesture() {
        let longTapGesture = UILongPressGestureRecognizer(target: self, action: #selector(longTap(gesture:)))
        collectionView.addGestureRecognizer(longTapGesture)
    }
    
    @objc func longTap(gesture: UILongPressGestureRecognizer) {
        switch gesture.state {
        // ロングタップの開始時
        case .began:
            guard let selectedIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) else {
                break
            }
            let movingCell = collectionView.cellForItem(at: selectedIndexPath) as! CustomCell
            // 並び替えるセルのインスタンスを保持
            celltoMove = movingCell
            collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
        // セルの移動中
        case .changed:
            collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: gesture.view))
        // セルの移動完了時
        case .ended:
            collectionView.endInteractiveMovement()
        default:
            collectionView.cancelInteractiveMovement()
        }
    }

並び替えを可とする

デリゲートメソッドcollectionView(_:canMoveItemAt:)trueを返します。

// 並び替えを可とする
    func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool {
        return true
    }

並び替え時のデータ処理

セルが移動された際に呼ばれるデリゲートメソッドcollectionView(_:moveItemAt:to:)を実装します。
ここで、CollectionView内のセルを取得し、配列に格納して、DBへの保存処理を行います。

// 並び替え時の処理
    func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
        // セル格納用の配列
        var cellsArray:[CustomCell] = []
        
        // CollectionViewに表示中のセルを取得
        for i in 0...collectionView.visibleCells.count {
            // 移動中のセルは、visibleCellsでは取得できない為スキップ
            if i == destinationIndexPath.row {
                continue
            }
            // セルを配列に格納、orderプロパティに並び順を格納
            let cell = collectionView.cellForItem(at: IndexPath(item: i, section: 0)) as! CustomCell
            cell.order = i
            cellsArray.append(cell)
        }
        // visibleCellsで取得漏れしてるセルを配列に追加
        let cellToMove = celltoMove as! CustomCell
        cellToMove.order = destinationIndexPath.row
        cellsArray.insert(cellToRemove, at: destinationIndexPath.item)
        // DBへの保存処理
        logicClass.updateCellsOrder(cells: cellsArray)
    }

次回表示時

DBからセルの取得処理を行って、order順に表示してあげます。

   func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // DBからセル取得し、order順に並び替え
        let cells = logicClass.getCellsfromDB()
        cells.sort{ $0.order > $1.order }
      
        return cells[indexPath.row]
    }

他に

iOS11以降であれば、UICollectionViewDragDelegateUICollectionViewDropDelegateを利用した実装方法も可能です。
機会があれば、こちらも紹介したいと思います。

さいごに

シンプルな並び替え実装は難しくないですが、色々と要件が重なると途端に難しくなりますね。色々な実装パターンを知っておいて、使い分けしたいですね。

おすすめ書籍

Yossy

シェア
執筆者:
Yossy
タグ: SwiftiOS

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前