春の入門祭りの第13弾です。
はじめに
TIG メディアユニットの福谷(ふくや)です。
お仕事では主にサーバーサイド領域で開発していますが、趣味でiOSアプリを開発しており、春の入門祭りの社内アナウンスがあったので書いてみようと思います。
iOSアプリで記事や写真などを一覧表示させたい場合、必ずと言っていいほどCollectionView(あるいはTableView)が採用されると思います。
iPhoneが発売された当初のデザインは、縦にコンテンツが並ぶだけのレイアウトでしたが、昨今はコンテンツの一覧性・視認性をより高めるために、縦にも横にもスクロールできるCollectionViewが一般的になってきています。
Compositional Layouts
そこでCompositional Layoutsの登場です。
Compositional LayoutsはWWDC2019に発表された複雑なレイアウトをシンプルに実装するための考え方です。CollectionViewにおいてはUICollectionViewCompositionalLayout
クラス 6を利用します。
Compositional Layoutsの詳しい解説はWWDC2019の動画 2を見るか、それを元に解説した記事 3もあるのでそちらを参照してください。また公式サンプルコード 7もかなり参考になるためおすすめです。
※公式ではiOS13でサポートされていますが、iOS13以前でも利用可能にするためのライブラリ 1がでています。
アプリを作る
以下のようなFuture Tech Blogリーダーを作ってみようと思います。
ソース全量はこちら。
※以降の解説はCompositional Layoutsの実装部分に焦点を当てて解説していきます。
item・group・sectionの構成を決める
Compositional Layoutは item・group・section、そしてsectionを内包するlayoutにより構成されます。
今回作るアプリのUIを例に、item・group・sectionをどう構成するかについて示したのが下記の画像です。
それではLayoutを書いていきましょう。
Compositional Layoutで実装する
まず大枠のSectionから書いていきます。
func createLayout() -> UICollectionViewLayout { |
sectionheaderSize
のwidthDimension
に引数として渡しているfractionalWidth(1.0)
は、sectionの横幅と同じ比率でheaderの横幅を定義することを意味します。 4
また、heightDimension
に引数として渡しているestimated(44)
は、44で高さを指定するものの最終的なレイアウトはレンダリング時に決定します(=「弱い定義」と勝手に呼んでいます)。 5
ソースコード中の余白定義①②③はそれぞれUI上の下記のポイントに対応しています。
続いてitem・groupのレイアウトを定義していきます。
func createLayout() -> UICollectionViewLayout { |
SectionLayoutKind
SectionLayoutKind
はアプリ中のおすすめ
や春の入門祭り
などのセクションをenumで定義したもので、
下記の通り定義しています。
enum SectionLayoutKind: Int { |
もし固定的にセクションを管理するならenumで定義しておくのが良いと思います。
一方でenumで定義するとenum内でセクションごとのレイアウト情報をいろいろ管理したくなりますが、createLayout()
内にもレイアウト定義をしているので、見通しを良くするためにも最低限のレイアウト情報のみenum内で定義すべきだと思います(この場合水平スクロールの挙動)
groupのレイアウト定義
let group: NSCollectionLayoutGroup |
(2)おすすめセクション以外ののgroup定義
NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitem: item, count: 2)
vertical
でcount
を”2”にすることによって垂直にitemを2つスタックしています。
(1)おすすめセクションのgroup定義
おすすめの記事は画像を大きくして目立たせたいので水平にitemを2つスタックしています。NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 2)
groupSizeの定義
let groupWidth = layoutEnvironment.container.effectiveContentSize.width - 15 * 2 |
layoutEnvironment.container.effectiveContentSize
はcollectionViewの描画領域を意味しています。今回は対応していませんが、例えばスマホの向きによってアプリのレイアウトを変えたい場合はこの値を使って分岐処理を書けば対応できます。
おわりに
以上が今回のアプリのレイアウト部分に関する解説になります。コメントを除いて38行で書けました。
同じレイアウトをUICollectionViewLayoutのカスタムクラスで実現しようとしたらかなりの行数・難易度になるのではないでしょうか。item・group・sectionの理解さえすれば私のような初学者でも簡易かつシンプルに実装できるので、今後のiOSアプリ開発でどんどん採用されていくのではと思います。
その際の参考になれば幸いです!
- 1.https://github.com/kishikawakatsumi/IBPCollectionViewCompositionalLayout ↩
- 2.https://developer.apple.com/videos/play/wwdc2019/215/ ↩
- 3.https://qiita.com/shiz/items/a6032543a237bf2e1d19#%E8%83%8C%E6%99%AF%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AE%E6%99%82%E4%BB%A3%E3%81%AE%E5%A4%89%E5%8C%96 ↩
- 4.https://developer.apple.com/documentation/uikit/nscollectionlayoutdimension/3199059-fractionalwidth ↩
- 5.https://developer.apple.com/documentation/uikit/nscollectionlayoutdimension/3199057-estimated ↩
- 6.https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout ↩
- 7.https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/using_collection_view_compositional_layouts_and_diffable_data_sources ↩