UICollectionViewLayoutの最新情報

Advances in Collection View Layout

https://developer.apple.com/videos/play/wwdc2019/215/

Sessionの概要

UICollectionViewLayoutでは魅力的なインタラクティブコレクションを簡単に構築出来ます。基本のリストから、高度な多次元のブラウジング体験に至るまで、複雑さの異なる動的で応答性の高いレイアウトを作成する方法について説明されている

UICollectionViewFlowLayoutのコンセプト

Composition Layout

  • Composable
  • Flexible
  • Fast
Composing small layout groups together

小さなレイアウトを組み合わせて大きなレイアウトを構成する

Hello World Composition Layout

let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(44.0))
let item = NSCollectionLayoutItem(layoutSize: size)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
let layout = UICollectionViewCompositionalLayout(section: section)
Item > Group > Section > Layout

Sizeの設定

Item, Groupを定義時に設定する
class NSCollectionLayoutDimension {
    class func fractionalWidth(_ fractionalWidth: CGFloat) -> Self
    class func fractionalHeight(_ fractionalHeight: CGFloat) -> Self
    class func absolute(_ absoluteDimension: CGFloat) -> Self
    class func estimated(_ estimatedDimension: CGFloat) -> Self
}
  • estimated
  • 推定size指定で、文字の大きさを自動で変更したい場合などに有用

Layoutの定義

GroupはItemの列や行を繰り返す構造になっている

Item毎に余白を設定する

let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)

NSCollectionLayoutSupplementaryItem

  • Badges
  • Headers
  • Footers

Headers と Footersの設定

  • Boundary supplementary item
  • Section or entire layout
  • Pinning
// NSCollectionLayoutBoundarySupplementaryItem
let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: "header", alignment: .top)
let footer = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: footerSize, elementKind: "footer", alignment: .bottom)

header.pinToVisibleBounds = true // 上部で固定
section.boundarySupplementaryItems = [header, footer]

Sectionのスクロール方法の設定

// Orthogonal Scrolling Sections
enum UICollectionLayoutSectionOrthogonalScrollingBehavior: Int {
    case none
    case continuous
    case continuousGroupLeadingBoundary
    case paging
    case groupPaging
    case groupPagingCentered
}
scroll_behavior_continuous

Sample code

func configureHierarchy() {
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.backgroundColor = .systemBackground
        view.addSubview(collectionView)
        collectionView.delegate = self
}


    //   +-----------------------------------------------------+
    //   | +---------------------------------+  +-----------+  |
    //   | |                                 |  |           |  |
    //   | |                                 |  |           |  |
    //   | |                                 |  |     1     |  |
    //   | |                                 |  |           |  |
    //   | |                                 |  |           |  |
    //   | |                                 |  +-----------+  |
    //   | |               0                 |                 |
    //   | |                                 |  +-----------+  |
    //   | |                                 |  |           |  |
    //   | |                                 |  |           |  |
    //   | |                                 |  |     2     |  |
    //   | |                                 |  |           |  |
    //   | |                                 |  |           |  |
    //   | +---------------------------------+  +-----------+  |
    //   +-----------------------------------------------------+

func createLayout() -> UICollectionViewLayout {

    let config = UICollectionViewCompositionalLayoutConfiguration()
    config.interSectionSpacing = 20

    let layout = UICollectionViewCompositionalLayout(sectionProvider: {
        (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        guard let sectionKind = SectionKind(rawValue: sectionIndex) else { fatalError("unknown section kind") }

        let leadingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.7), heightDimension: .fractionalHeight(1.0)))
        leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)

        let trailingItem = NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(0.3)))
        trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
        let trailingGroup = NSCollectionLayoutGroup.vertical(layoutSize: NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.3), heightDimension: .fractionalHeight(1.0)),
                                                             subitem: trailingItem,
                                                             count: 2)

        let orthogonallyScrolls = sectionKind.orthogonalScrollingBehavior() != .none
        let containerGroupFractionalWidth = orthogonallyScrolls ? CGFloat(0.85) : CGFloat(1.0)
        let containerGroup = NSCollectionLayoutGroup.horizontal(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(containerGroupFractionalWidth),
                                              heightDimension: .fractionalHeight(0.4)),
            subitems: [leadingItem, trailingGroup])
        let section = NSCollectionLayoutSection(group: containerGroup)
        section.orthogonalScrollingBehavior = sectionKind.orthogonalScrollingBehavior()

        let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
            layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .estimated(44)),
            elementKind: OrthogonalScrollBehaviorViewController.headerElementKind,
            alignment: .top)
        section.boundarySupplementaryItems = [sectionHeader]
        return section

    }, configuration: config)
    return layout
}
最新情報をチェックしよう!