こんにちは
ZOZO #6 Advent Calendar 2022 21日目の記事を担当します (去年も21日)
今年は遅れてスノボーにシーズインした、@koiwai2020です
よろしくお願いします
今回も
大好きなスノボーシーズンに使えそうな話題が、、、、作れませんでした。
なので調べてみたものの使うことはなかった、CompositionalLayoutで作ったカルーセルの無限スクロールを忘れないように記す場にします。
無限スクロールの実装について
無限スクロールは、動画のように1〜5の数字が割り振られたセルが、無限にスクロールされます。
— 小岩井 (@WfODXAd0jmop1Ev) December 14, 2022
この挙動をCompositionalLayoutで実装する2つのポイントを紹介します。
①セルを表示したいコンテンツの2倍用意する
今回実際に表示させたいコンテンツは、1〜5の数字が割り当てられた、5つのセルです。
ですが、無限スクロールを実装するにあたって、こちらを2倍用意します。
②端まで進んだら戻るを実装する
実際に無限にセルを増やし続けることは難しいです。
その為、カルーセルの両端までスクロールされたタイミングで、真ん中の繋ぎ目のセルの位置まで戻します。
これを繰り返すことで、スクロールが無限に続いているように見せます。
こちらは、次の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
}
}
ざっくりですが、以上で無限スクロールっぽい挙動になります。