UICollectionViewLayoutについて - 準備編 -
UICollectionViewのDocumentを読んだので、覚え書き程度にまとめました。
登場するメソッドで何をするのかをまとめたもので、具体的な実装方法等(コード)は一切書いていません。具体的な実装方法を知りたい方は、実装編を参照してください。
既知のことが多いと思いますので、流し読みしてください。
- UICollectionViewLayoutとは
- UICollectionViewを構成する視覚要素
- レイアウトを定める
- Layout Performanceの最適化
- (アニメーションを付ける)
- Self-Sizing (Auto Layout)
UICollectionViewLayoutとは
UICollectionView
のCell
などを、どの位置にどんな状態で表示するかを決定するオブジェクト
UICollectionView
のLayoutを調整するものとして、
- UICollectionViewFlowLayout - UICollectionViewDelegateFlowLayout
などがある。
その中で最も自由度の高い方法がUICollectionViewLayout
を作成するやり方。
どれを選べば良いかはCookpadさんがブログで書いてくれています。
今回はこのUICollectionViewLayout
について紹介します。
UICollectionViewを構成する視覚要素
UICollectionViewにはレイアウトが必要な3種類の視覚要素がある。
- Cells - Supplementary views - Decoration views
UICollectionViewは様々なタイミングでこれらの視覚要素のレイアウト情報を提供するようにLayoutオブジェクトに依頼する。
Cells
セルはレイアウトによって位置を決める主要要素。データによってはセクション毎に分けたりする。
レイアウトオブジェクトの主な仕事はUICollectionViewのViewの中身にセルを配置すること。
Supplementary Views
Header
, Footer
といったCellとは違うもの。これもUICollectionViewLayout
によって指定しなければならない。(optional)
Decoration Views
装飾するView。Supplementary views
に似ているが少し違う。
こちらで場所を指定できる。(セル間の区切り線 etc..)
レイアウトを定める
視覚要素(Cells
, Supplementary Views
, Decoration Views
)をどのように配置するかを決めるのが以下のメソッド。
- collectionViewContentSize - prepare() - layoutAttributesForElements(in:) - layoutAttributesForItem(at:) - layoutAttributesForSupplementaryView(ofKing: at:) - layoutAttributesForDecorationView(ofKing: at:)
UICollectionView自体のレイアウトが変更されたり、Cellが移動・追加・削除されるたびに新しいレイアウト情報を得るために呼び出される。
collectionViewContentSize
Collection viewの全体のheight
, width
を返す。
prepare()
layoutAttributesForElements(in:)
は頻繁に呼び出されるため、なるべく計算処理を含まない方が良い。
そこで、レイアウト情報が更新される前に呼ばれるprepare()
をOverrideして、レイアウト情報を求める計算処理を記述するのが普通である。
layoutAttributesForElements(in:)
指定された短形内の全てのセルとViewのLayoutAttributes
を返す。
Cells
, Supplementary views
, Decoration views
の全てのLayoutAttributes
を返す必要がある。
下記のメソッドを実装することで、それぞれの要素ごとに責務を分割できる。
これらは自分で呼び出さない限り実行されない
- layoutAttributesForItem(at:) - layoutAttributesForSupplementaryView(ofKing: at:) - layoutAttributesForDecorationView(ofKing: at:)
自分はこれらのメソッドを使わず、自分で実装した方が楽な時の方が多い気がします。
Layout Performanceの最適化
Custom layoutを作る時、実際に変更された部分だけを無効・更新するように書くことでパフォーマンスをあげることができる。
invalidateLayout()
を呼び出すことで、レイアウト情報のみを更新することができる。(reloadData()
はDataSourceとレイアウトを更新する。)
更にUICollectionViewLayoutInvalidationContextのサブクラスを作成し用いることで、より無効・更新する箇所を限定することができる。
以下で最適化するのに必要なメソッドを紹介する。
- shouldInvalidateLayout(forBoundsChange:) - invalidationContext(forBoundsChange:) - shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:) - invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
shouldInvalidateLayout(forBoundsChange:)
CollectionView自体のレイアウトが変更された時に、中のレイアウトを更新するかどうかを決める。
更新する時はtrue
、しなくていい時はfalse
を返す。
true
を返された場合はinvalidationContext(forBoundsChange:)
が呼び出され、この返り値のUICollectionViewLayoutInvalidationContext
を引数にinvalidateLayout(with:)
を呼び出し、現在のレイアウトを無効にしてからprepare()
を呼び出す。
invalidationContext(forBoundsChange:)
ここではUICollectionViewLayoutInvalidationContext
を変更に応じて編集し、返す。
このContextをもとにinvalidateLayout(with:)
でキャッシュしたレイアウト情報を削除する。
invalidateLayout(with:)
現在のレイアウト情報によるレイアウトを無効にする。
その際に、引数のUICollectionViewLayoutInvalidationContext
の情報から更新しなければいけない箇所をを読み取り、事前に計算したレイアウト情報を修正する。(計算後にキャッシュしたレイアウト情報を削除する等々...)
shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)
Self-sizing(AutoLayout)を行うCellについて、preferredLayoutAttributesFitting(_:)
で得られたLayoutAttributes
がlayoutAttributesForElements(in:)
で指定したものと異なる場合に呼び出される。
先ほど紹介したshouldInvalidateLayout(forBoundsChange:)
の引数が変ったもの。
同様に、true
を返すと更新処理に遷り、false
を返すと何もしない。
true
を返すとinvalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
が呼び出される。
Self-sizing (AutoLayout)
の具体的な説明は後述する。
invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)
先ほどのinvalidationContext(forBoundsChange:)
と同様のことを行う。
(アニメーションを付ける)
アニメーションをつける場合は、これらのメソッドをオーバーライドして適切なレイアウト情報を提供する必要がある。
- initialLayoutAttributesForAppearingItem(at:) - initialLayoutAttributesForAppearingSupplementaryElement(ofKing: at:) - initialLayoutAttributesForAppearingDecorationElement(ofKing: at:) - finalLayoutAttributesForDisappearing~(at:)
さらに、
- finalizeCollectionViewUpdates()
をオーバーライドすることで、全体にアニメーションを追加したり、レイアウト関連の最終タスクを実装したりすることもできる。
これらについては、本記事では割愛する。
Self-Sizing (Auto Layout)
Self-SizingによってCellのSizeが変更になった時、shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)
が呼び出され、レイアウトを更新するか指定できる。
引数で与えられるforPreferredLayoutAttributes
は、preferredLayoutAttributesFitting(_:)
の返り値、つまり、AutoLayoutによって測定されたCGSize
が含まれる。
それをキャッシュし、用いることによってUITableView
のようなSelf-Sizingを行うことができる。
具体的な実装については実装編を参照してください。
おわり。