TL;DR
- collectionViewにlayoutMarginを設定する。
- compositionalLayoutのsectionでcontentInsetsReferenceをlayoutMarginsにする。
- sectionのinsetをスッキリ統一。
CompositonalLayoutのsection構築におけるinsetの管理問題。
CompositonalLayoutとはiOS13から登場したUICollectionViewのレイアウト構築用フレームワーク(?)です。
以下のようにsection、group、itemによってレイアウトを構築できます。
func createLayout() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in
let section: NSCollectionLayoutSection
let sectionType = SectionLayoutKind.allCases[sectionNumber]
switch sectionType {
case .name:
let item = NSCollectionLayoutItem(
layoutSize: .init(
widthDimension: .fractionalWidth(1),
heightDimension: .estimated(44)
)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: .init(
widthDimension: .fractionalWidth(1),
heightDimension: item.layoutSize.heightDimension
),
subitems: [item]
)
section = .init(group: group)
section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16) // insetを調整
case .desc:
...
}
return section
}
return layout
}
しかしcaseが増えてくるとsectionごとに以下のようなinsetを指定するのが面倒になってきます。
section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16)
sectionを一時変数にしてreturnする前に設定すれば上記のように何度もinsetを指定することは回避できます。
func createLayout() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in
let section: NSCollectionLayoutSection
let sectionType = SectionLayoutKind.allCases[sectionNumber]
switch sectionType {
case .name:
...
section = .init(group: group)
case .desc:
...
section = .init(group: group)
}
section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16)
return section
}
return layout
}
しかし、上記の方法だとsectionごとにちょっと上下の幅を増やしたりができません。
一応個別にsectionにinsetを指定し、途中でreturnさせれば特定のsectionだけ別のinsetにできます。
func createLayout() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in
let section: NSCollectionLayoutSection
let sectionType = SectionLayoutKind.allCases[sectionNumber]
switch sectionType {
case .name:
...
section = .init(group: group)
case .desc:
...
section = .init(group: group)
section.contentInsets = .init(top: 16, leading: 16, bottom: 16, trailing: 16)
return section // 個別にreturn
}
section.contentInsets = .init(top: 8, leading: 16, bottom: 8, trailing: 16)
return section
}
return layout
}
けど他のsectionのinsetと横幅がずれないようにしたりするために結局defaltのinsetの幅を確認しに行ったりするのが手間です。
理想はデフォルトのinsetはそのままにsectionごとに幅のプラスやマイナスを指定できたらいいのになと思いました。
LayoutMargingsを使えばできた
NSCollectionLayoutSectionにはcontentInsetsReferenceというプロパティが生えています。
これに.layoutMargingsを指定すると、CollectionViewのlayoutMarginをデフォルトのinsetとして使うようになります。
collectionView.layoutMargins = .init(top: 8, left: 16, bottom: 8, right: 16)
section.contentInsetsReference = .layoutMargins
するとsectionで指定したinsetはこのlayoutMarginからと足された値になります。
reference指定なし | referenceにlayoutMargingsを指定 |
---|---|
例えば、layoutMargingsが以下の場合
collectionView.layoutMargins = .init(top: 8, left: 16, bottom: 8, right: 16)
以下のnameというsectionのtopのinsetは
marginの8とsectionProvider内で指定した8の合計で16になります。
2021/4/20: 訂正
すみません。。。後日動作確認していて気づいたのですが、contentInsetsReferenceにlayoutMargings指定することで設定できるデフォルトのinsetは横幅のみでした。
上下のinsetはlayoutMargingの値に関わらずデフォルトだと0になります。
そのため、上下のinset調整は個別に行う必要がありますが、
横幅のinsetにデフォルトを設けつつ、セクションごとに個別にreturnしなくてもよくなるというメリットは変わりません。
func createLayout() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { sectionNumber, _ -> NSCollectionLayoutSection in
let section: NSCollectionLayoutSection
let sectionType = SectionLayoutKind.allCases[sectionNumber]
switch sectionType {
case .name:
...
section = .init(group: group)
section.contentInset.top = 8.0
}
section.contentInsetsReference = .layoutMargins // (top: 8, left: 16, bottom: 8, right: 16)がデフォルトのinsetになる。
return section
}
return layout
}
これで「デフォルトのinsetは保ちつつ、sectionごとにinsetを調整する」ということが楽にできるようになりました。
結論
- contentInsetsReferenceに.layoutMarginsを使うとCollectionViewのlayoutMarginをデフォルトのinsetにできる
- sectionにcontentInsetsを指定すればデフォルトのinsetを増減させられるので個別にinsetを指定したsectionをreturnしなくてよくなる。