目次
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)

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
}





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
}