Compositional Layout은 item, group, section, layout으로 구성된다.
Item > Group > Section 순서로 각 요소들의 크기와 위치를 정의하고 지정하여 Compositional Layout을 구축할 수 있다.
1. fractionalWidth & fractionalHeight
컨테이너의 너비 또는 높이에 대한 비율로 크기를 정의
2. absolute
절대적인 포인트 값으로 크기를 정의
3. estimated
크기가 정확하지 않을 때 사용되며, 추정 값을 제공
let item1Size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5), heightDimension: .fractionalHeight(1.0))
let item1 = NSCollectionLayoutItem(layoutSize: item1Size)
let item2Size = NSCollectionLayoutSize(widthDimension: .absolute(100), heightDimension: .absolute(50))
let item2 = NSCollectionLayoutItem(layoutSize: item2Size)
let item3Size = NSCollectionLayoutSize(widthDimension: .estimated(100), heightDimension: .estimated(50))
let item3 = NSCollectionLayoutItem(layoutSize: item3Size)
horizontal, vertical, custom
NSCollectionLayoutGroup.horizontal(layoutSize:subitems:)
NSCollectionLayoutGroup.vertical(layoutSize:subitems:)
NSCollectionLayoutGroup.custom(layoutSize:itemProvider:)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(0.2))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
// Inset 줄 경우
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 14, bottom: 16, trailing: 14)
Section Provider 클로저로 섹션마다 다양한 레이아웃 정의 가능
sectionIndex: 현재 처리 중인 섹션의 인덱스
layoutEnvironment: 현재 레이아웃의 환경을 나타내는 객체 (디바이스의 방향, 크기 등의 추가 정보)
클로저 내부에서는 이 매개변수들을 사용하여 조건에 따라 적절한 NSCollectionLayoutSection
객체를 생성하고 반환한다.
이 방식은 매우 유연하며, 런타임에 따라 레이아웃을 조정해야 하는 복잡한 UI를 구현할 때 유용하다.
// 단일 섹션 레이이아웃 생성
init(section: NSCollectionLayoutSection)
let layout = UICollectionViewCompositionalLayout(section: section)
// 섹션 프로바이더를 사용한 레이아웃 생성
init(sectionProvider: @escaping SectionProvider)
return layout
item Cell: 메인 데이터를 표시하는데 사용하는 UI
supplementaryView: 메인 데이터를 보충해주는 UI (헤더나 푸터),
한 섹션을 기준으로 9가지의 방향으로 표현이 가능하다.
decorationView: 데이터를 표출하지 않고 단순히 레이아웃을 꾸미는데 사용되는 UI (컬렉션뷰의 모델과 독립적인 뷰, 배경, 섹션 하이라이트 등)
먼저 UICollectionReusableView
를 상속받은 뷰를 생성하고 register
하고 DataSource Delegate
를 사용해 셀을 구현한다.
collectionView.register(:forSupplementaryViewOfKind:withReuseIdentifier:)
view.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView")
Delegate 메소드는 아래와 같이 구현한다.
dequeueReusableSupplementaryView(ofKind:withReuseIdentifier:for:)
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
switch kind {
case UICollectionView.elementKindSectionHeader:
let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderView
header.prepare(text: "헤더 타이틀")
return header
case UICollectionView.elementKindSectionFooter:
let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "FooterView", for: indexPath) as! FooterView
return footer
default:
return UICollectionReusableView()
}
}
Layout 안에서 header, footer에 관한 레이아웃을 정의하고,
NSCollectionLayoutBoundarySupplementaryItem
인스턴스를 생성한 뒤 section.boundarySupplementaryItems
에 인스턴스를 주입해 사용한다.
let section = NSCollectionLayoutSection(group: group)
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(100.0))
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
section.boundarySupplementaryItems = [header]
return section
DecorationView는 섹션별로 적용 가능하다.
SupplementaryView와 동일하게 UICollectionReusableView
를 상속하는데 decorationView는 데이터를 나타내지 않으므로 데이터를 반영하지 않아도 되는 뷰를 준비한다.
SupplementaryView와는 다르게 collectionView.register과 dataSource 델리게이트를 사용하지 않고, layout에서 register
해서 사용한다.
func getLayout() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout { (section, env) -> NSCollectionLayoutSection? in
...
layout.register(MyDecorationView.self, forDecorationViewOfKind: "MyDecorationView")
return layout
}
section에 decorationItems
프로퍼티에 데코레이션 뷰를 주입하여 적용한다. 시스템에서 제공하는 아래 메소드를 사용하여 elementKind 값으로 위에서 등록한 인스턴스를 획득한다.
open class func background(elementKind: String) -> Self
// in getLayout()
// Section
let section = NSCollectionLayoutSection(group: group)
// Decoration
let decorationView = NSCollectionLayoutDecorationItem.background(elementKind: "MyDecorationView")
decorationView.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
section.decorationItems = [decorationView]
return section
참고: 참고