UICollectionViewLayoutを使ったときのUICollectionViewの描画サイクル

# はじめに
こんにちは、iOSエンジニアの dayossi です。

家族が幸せになれるサービスを提供したいと思って、
HaloHaloという家族日記アプリをリリースしています。


今回は、いまさらながら
UICollectionViewの描画タイミングの全体像について
整理したいと思います。
（ 詳しいコードなどは、ほかの方の記事がわかりやすいので
  最後にご紹介しております ）

# CollectionViewのレイアウトをいじりたいけど…

CollectionViewのレイアウトを
もっといろいろカスタマイズしたいなーと思って
UICollectionViewLayoutで一からレイアウトを組んでみたら、


「どのタイミングでレイアウト更新したらいいんだっけ？」
「セルに載せたUILabelとかUIImageViewのデータ更新もしたいけど、
 どのタイミングでセルの作成とか更新とかしてるんだ？」

と、途中からわからなくなってきたので整理しました。


# UICollectionViewがCellを表示する大まかな流れ

大きな流れとしては
***CollectionView全体のレイアウトを決めてから、セルを作る作業に入ること***
が大事かなと思いました。


まず、最初にCollectionViewが作られるときは
以下の ```UICollectionViewDataSource``` のメソッド（濃いグレー）が呼ばれます。

ここでは、
画面いっぱいに映る数 + 次に表示される 画面半分だけの数の
```CollectionViewCell```( 以下 セル ) が作られます。


![スクリーンショット 2020-09-23 20.18.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/3ad79f37-7743-67ac-9dde-9c67dd876bae.png)

なお、以下のメソッドはオプションなので
書かなくてもいいものです。
・ ```CollectionViewCell.prepareForReuse()``` 
・ ```DataSource.prefetch（）``` (iOS 10から追加） 





この中で、レイアウトを指定するメソッドも呼ばれています。

呼ばれるタイミングは、
1. ```DataSource.numberOfItemsInSection``` の前からです。
（薄いグレー ： ```UICollectionViewLayout``` のメソッドです）



![スクリーンショット 2020-09-23 20.16.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/7ad8b835-59e2-a5d5-1ab1-ba57b5538155.png)







( ```DataSource.willDisplay()``` はオプションメソッドなので
  記述しなくても動きます )

注：```layoutAttributesForItem()```のタイミングは、
　  （初期設定時は厳密に）ハッキリとわかりませんでした。あくまで予測です。


以上から
***CollectionView全体のレイアウトを決めてから、セルを作る作業に入る***
という流れが見えると思います。

************************************

ここからCollectionViewを下へスクロールすると、
以下のようにメソッドが呼ばれます。
![スクリーンショット 2020-09-23 20.46.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/6f1732fb-aba5-d229-beb0-cbbeb474c606.png)


（```DataSource.didEndDisplay()``` はオプションメソッドなので
  記述しなくても動きます )


基本的な流れは変わりませんが、上に消えていったセルは
初期設定時に準備された 緑色のセル の次に格納されるのがポイントです。



# UICollectionViewLayoutを再度呼び出したいとき

先ほどの図のように
先に作ったセルを再利用して、スクロールした先のセルは表示されるので

特に書き換える処理がなければ
消えたセルの内容がそのまま次に出てくるセルに使用されます。

![スクリーンショット 2020-09-23 20.46.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/6f1732fb-aba5-d229-beb0-cbbeb474c606.png)

なので、表示したいものにあわせてレイアウトを変えたい場合は
もういちど計算しなおす必要がでてきます。

再利用するセルのレイアウトを変えたいときは、
```collectionView.invalidLayout（）```を呼ぶ必要があります。

![スクリーンショット 2020-09-23 20.46.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730098/4b6ac6f2-96b8-8b77-8790-833af9491e9c.png)

``DataSource.prepareForReuse()``の中で
``collectionView.invalidLayout（）``を呼ぶと、
次にセルを再利用する際に、レイアウトをスムーズに変更できると思います。


また、レイアウト・UILabelなどのプロパティUI表示も変更したい場合は
``DataSource.prepareForReuse()``の中で、更新対象のプロパティにnilを代入したあと

任意のタイミングで
``collectionView.reloadData()``
``collectionView.reloadSections( )``
のようなリロード処理を呼び出すと、まるっと対応可能です。


ただ、```collectionView.reloadData()```よりも
```collectionView.reloadSections()```で
必要な部分だけ更新するやり方のほうが
メモリに優しい印象です。


# 終わりに

UICollectionViewのレイアウトは、iOS13から使用可能な
Compositional Layoutを使えば、
レイアウトのカスタマイズは結構カンタンにできます。
（私もコレに任せて、HaloHaloを作成しました）


基本的なところを改めて学び直してみると
OSバージョンの壁を超えて、より多くの方に
より使いやすいUIを考えられると思いました。

まだまだ未熟者なので、
「ここはこう考えたほうがいいよ！」など
温かいご指摘などを頂けますと幸いです。

最後までお読みいただき、ありがとうございます。

# 参考記事
***ちょっと長いですが、はじめに見たほうが良かったです…!!***
[A Tour of UICollectionView (WWDC 2018)](https://developer.apple.com/videos/play/wwdc2018/225/)
[What's New in UICollectionView in iOS 10 (WWDC 2016)](https://developer.apple.com/videos/play/wwdc2016/219)

***具体的に考えていく際に、とても参考になりました!!***
[[iOSDC 2016] iOS10のCollectionViewからライフサイクルが変わったので遊んでみた](https://tech.recruit-mp.co.jp/event/post-10497/)
[【Swift】CollectionViewを再理解する](https://qiita.com/shiz/items/2e9771fe56e39544ff3e)
[CollectionViewのカスタムレイアウトを作ってみた](https://re-engines.com/2019/10/19/collectionview%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F/)
[UICollectionView の Layout で悩んだら](https://techlife.cookpad.com/entry/2017/06/29/190000)
[UICollectionViewのカスタムレイアウトの作り方](https://qiita.com/takehilo/items/d49069572c848df9258a)