UICollectionLayoutListConfigurationのheaderMode=.firstItemInSection観測隊
はじめに
UICollectionView
は UITalbeView
よりも出来ることが多いですよ、ということでAppleが推している UICollectionView
であるが、機能の荒波に溺れないように手軽な仕様も用意してある。次のものを見ていただきたい。あるソースコードとその実行結果である。
「甲信越」など、ひとつめのデータがちょっと違う色と大きさで表示されている。これは、1個目のデータをヘッダー扱いにする機能を使っている。これは UICollectionView
に備わっている機能である。今日はこれを観測していきたい。
UICollectionViewのコンフィグレーション
この仕様にするためには UICollectionView
にコンフィグレーションというものを使う。コンフィグレーションとは仕様を細かく設定するものである。コンフィグレーションを使った UICollectionView
のインスタンス生成の様子は次の通り。
var collectionView: UICollectionView = {
var listConfiguration =
UICollectionLayoutListConfiguration(
appearance: .plain)
listConfiguration.headerMode = .firstItemInSection
let simpleLayout =
UICollectionViewCompositionalLayout.list(
using: listConfiguration)
return UICollectionView(
frame: .zero,
collectionViewLayout: simpleLayout)
}()
コンフィグレーションの中でも UICollectionLayoutListConfiguration
を使う。UICollectionLayoutListConfiguration
はiOS14からなのでわりと新しいものである。
ここで listConfiguration.headerMode = .firstItemInSection
というのが、ひとつめのものをヘッダーとして使う設定にしている箇所である。
UICollectionLayoutListConfigurationの設定よって見た目が違う
UICollectionLayoutListConfiguration(appearance: .plain)
の引数に与えるものによってヘッダーのデザインが変わる。
.plain
.grouped
.insetGrouped
.sidebar
.sidebarPlain
sidebarは文字が大きいですな。
新潟を選択状態にした理由は、選択していないと .plain
と .sidebarPlain
はそもそも何が違うのかと思われてしまうから。
タップする
「甲信越」の部分をタップすると通常のセルと同じように collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
が走る。
我々の任務はヘッダーの扱いの調査だったので今日の観測はここまでである。ここからはソースコードの他の部分について書いたもの。記事を書いている時点でモダンな UICollectionView
の書き方に詳しいわけではないので、古い書き方が混ざっている。
UICollectionViewその他
collectionView.register(
UICollectionViewListCell.self,
forCellWithReuseIdentifier: cellId)
collectionView.dataSource = self
collectionView.delegate = self
セルの形式を UICollectionViewListCell
にする。cellId
は Cell
という文字列。
データソース
func numberOfSections(
in collectionView: UICollectionView) -> Int {
prefectures.count
}
func collectionView(
_ collectionView: UICollectionView,
numberOfItemsInSection section: Int) -> Int {
prefectures[section].count
}
func collectionView(
_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: cellId,
for: indexPath) as! UICollectionViewListCell
var cellConfiguration = cell.defaultContentConfiguration()
cellConfiguration.text = prefectures[indexPath.section][indexPath.row]
cell.contentConfiguration = cellConfiguration
return cell
}
セルを取得するが先ほど UICollectionViewListCell
で取得できるようにしたので、それが返ってくる。その UICollectionViewListCell
の defaultContentConfiguration()
は UIListContentConfiguration
を返す。これはセルの中身を設定するもので、文字列や画像などの入れ物があらかじめ用意されている。
Listなんとか、ありすぎじゃね?
-
UICollectionLayoutListConfiguration
UICollectionViewの性質を扱うコンフィグレーション -
UICollectionViewListCell
セルの型 -
UIListContentConfiguration
セルの性質を扱うコンフィグレーション
この3つの組み合わせ以外を試そうかとも思うがあまり面白い結果にならなさそう。
ViewController全体
import UIKit
class ViewController: UIViewController {
let prefectures = [
["北海道", "北海道"],
["東北", "青森", "岩手", "秋田", "宮城", "山形", "福島"],
["関東", "茨城", "栃木", "群馬", "埼玉", "千葉", "東京", "神奈川"],
["甲信越", "新潟", "長野", "山梨"],
["北陸", "富山", "石川", "福井"],
["東海", "岐阜", "静岡", "愛知", "三重"],
["近畿", "滋賀", "京都", "奈良", "大阪", "和歌山", "兵庫"],
["中国", "鳥取", "島根", "岡山", "広島", "山口"],
["四国", "香川", "徳島", "愛媛", "高知"],
["九州", "福岡", "佐賀", "長崎", "大分", "熊本", "宮崎", "鹿児島"],
["沖縄", "沖縄"]
]
let cellId = "Cell"
var collectionView: UICollectionView = {
var listConfiguration = UICollectionLayoutListConfiguration(appearance: .sidebar)
listConfiguration.headerMode = .firstItemInSection
let simpleLayout = UICollectionViewCompositionalLayout.list(using: listConfiguration)
return UICollectionView(frame: .zero, collectionViewLayout: simpleLayout)
}()
override func viewDidLoad() {
super.viewDidLoad()
collectionView.register(UICollectionViewListCell.self, forCellWithReuseIdentifier: cellId)
collectionView.dataSource = self
collectionView.delegate = self
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
}
}
extension ViewController: UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
prefectures.count
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
prefectures[section].count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! UICollectionViewListCell
var cellConfiguration = cell.defaultContentConfiguration()
cellConfiguration.text = prefectures[indexPath.section][indexPath.row]
cell.contentConfiguration = cellConfiguration
return cell
}
}
extension ViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("didSelect", prefectures[indexPath.section][indexPath.row])
}
}
Discussion