6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ZOZOAdvent Calendar 2022

Day 21

CompositionalLayoutのカルーセルを無限スクロールさせてみる

Last updated at Posted at 2022-12-20

こんにちは:christmas_tree:
ZOZO #6 Advent Calendar 2022 21日目の記事を担当します:snowflake: (去年も21日)
今年は遅れてスノボーにシーズインした、@koiwai2020です:snowboarder:
よろしくお願いします:bow:

今回も

大好きなスノボーシーズンに使えそうな話題が、、、、作れませんでした。
なので調べてみたものの使うことはなかった、CompositionalLayoutで作ったカルーセルの無限スクロールを忘れないように記す場にします。

無限スクロールの実装について

無限スクロールは、動画のように1〜5の数字が割り振られたセルが、無限にスクロールされます。

この挙動をCompositionalLayoutで実装する2つのポイントを紹介します。

①セルを表示したいコンテンツの2倍用意する

今回実際に表示させたいコンテンツは、1〜5の数字が割り当てられた、5つのセルです。

実際に表示させたい.png

ですが、無限スクロールを実装するにあたって、こちらを2倍用意します。

2倍.png

②端まで進んだら戻るを実装する

実際に無限にセルを増やし続けることは難しいです。
その為、カルーセルの両端までスクロールされたタイミングで、真ん中の繋ぎ目のセルの位置まで戻します。

左端までスクロールした時
戻る.png

右端までスクロールした時
戻る.png

これを繰り返すことで、スクロールが無限に続いているように見せます。
こちらは、次の2つを組み合わせることで実現しました。

  1. 端までスクロールされたかを検知する:visibleItemsInvalidationHandler 1
  2. 繋ぎ目のセルまで戻す:scrollToItem(at: at: animated:) 2

まず、visibleItemsInvalidationHandlerですが、こちらはカルーセルのスクロールをハンドリングすることが可能で、次の3つの情報から状態を判別することができます。

  • 表示中のセル:[NSCollectionLayoutVisibleItem]
  • セルの座標:CGPoint
  • 表示中のセルのレイアウト NSCollectionLayoutEnvironment

今回は、セルの座標を使って「端までスクロールされたか?」を確認しました。

あとは、検知されたタイミングで scrollToItem(at: at: animated:) を呼んでスクロール位置を移動させます。
この時、アニメーションの設定を無効にすることで、切り替わりを誤魔化します。

func createLayout() -> UICollectionViewLayout {
    UICollectionViewCompositionalLayout { [weak self] _, layoutEnvironment -> NSCollectionLayoutSection? in
        guard let self = self else { return nil }

        let horizonalMargin = 20.0
        let layout = carouselLayout(with: layoutEnvironment)

        // カルーセル内のスクロールをハンドリング
        layout.visibleItemsInvalidationHandler = { [weak self] _, point, _ in
            guard let self = self else { return }

            // マージンを除いた横幅
            let width = layoutEnvironment.container.contentSize.width - horizonalMargin * 2

            if let index = Int(exactly: (point.x + horizonalMargin) / width) {
                if index == 0 {
                    // 「左端までスクロールした時」の状態
                    self.collectionView.scrollToItem(at: IndexPath(item: 5, section: 1), at: .none, animated: false)
                } else if index == 9 {
                    // 「右端までスクロールした時」の状態
                    self.collectionView.scrollToItem(at: IndexPath(item: 4, section: 1), at: .none, animated: false)
                }
            }
        }
        return layout
    }
}

ざっくりですが、以上で無限スクロールっぽい挙動になります。

  1. https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199096-visibleitemsinvalidationhandler

  2. https://developer.apple.com/documentation/uikit/uicollectionview/1618046-scrolltoitem

6
6
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
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?