LoginSignup
6
3

More than 3 years have passed since last update.

[Swift5]CompositonalLayoutのinset管理にlayoutMarginsを使うと楽になる

Last updated at Posted at 2021-04-16

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しなくてよくなる。
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3