LoginSignup
9
8

More than 1 year has passed since last update.

[Swift] Compositional Layouts の Inner スクロールを検知する

Posted at

前置き

特定の実装をする際に、スクロールのハンドリングをしようと考えていました。

Compositional Layouts では簡単にできると思っていましたが、案外情報がなかったので(英語での情報はちらほら)、自分へのメモ的な部分も含めて記載していきます。

1. Inner Scrollとは?

Inner Scrollという名前があるわけではなく、わかりやすくするために呼んでいます。

Screen Shot

このように入れ子の構造になった画面でのスクロールを指します。

2. 現在地を取得する際の問題点

今までは、このような入れ子の構造を実装する場合
1. CollectionView in TableView
2. ScrollView + StackView
などで実現できるかと思います。

1 の場合は、中にあるCollectionViewから親にDelegateを渡してスクロールを検知できます。
2 の場合は、indexPathsForVisibleItemsvisibleCellsを使って計算することになります。

Compositional Layoutsで入れ子の構造を実装した場合、DelegateInnerのスクロールに反応してくれません
2 と同じようにCollectionViewのプロパティを使って計算することもできますが、とても冗長な実装になります。

3. 解決策

セクションのプロパティである visibleItemsInvalidationHandler を使うことで解決できます。
apple

この NSCollectionLayoutSectionVisibleItemsInvalidationHandler の中身ですが(名前がクソ長い)

public typealias NSCollectionLayoutSectionVisibleItemsInvalidationHandler = (
    [NSCollectionLayoutVisibleItem], 
    CGPoint,
    NSCollectionLayoutEnvironment
) -> Void

となっています。1つずつ見ていきましょう。

① NSCollectionLayoutVisibleItem

スクロールされた際に表示されている、セルの情報が入っています。
item

② CGPoint

これはそのままですが、スクロールされた際に表示されている、セルの現在地の座標を取得することができます。
Screen Shot swipe

③ NSCollectionLayoutEnvironment

スクロールされた際に表示されている、セルのレイアウト情報が入っています。
Screen Shot layout

現在値を取得

② のCGPointを使って計算することもできます。

または、① の NSCollectionLayoutVisibleItem にはIndexPath の情報が含まれているので、こちらを使用する方が内部的に計算がされており、実装は楽になるでしょう。

ただし、使い方に少しだけ注意が必要です。

index

セルが複数表示されている場合は、余計なIndexも取得されて複数で返ってくるので、その部分の計算は必要になってきます。基本的には、配列の最後に返ってくるIndexが現在地のIndexになります。

終わりに

一見すると便利な API が用意されてそうでしたが、挙動が少し怪しい?感じもあり...
Compositional Layouts も万能ではないということですね

SwiftUI もスクロールのハンドリングは自前で実装したりするので、Apple にはこの辺の実装を強化して欲いなぁと思ってしまった感じがあります。

参考文献

その他

9
8
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
9
8