iOS - Compositional Layout CollectionView

이한솔·2023년 12월 3일
0

iOS 앱개발 🍏

목록 보기
36/49

Compositional Layout CollectionView

Compositional Layout은 item, group, section, layout으로 구성된다.
Item > Group > Section 순서로 각 요소들의 크기와 위치를 정의하고 지정하여 Compositional Layout을 구축할 수 있다.



NSCollectionLayoutSize

Item 크기 지정하는 방법

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)

Group의 종류

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])

Section

let section = NSCollectionLayoutSection(group: group)

// Inset 줄 경우
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 14, bottom: 16, trailing: 14)

Layout

Section Provider 클로저로 섹션마다 다양한 레이아웃 정의 가능

sectionIndex: 현재 처리 중인 섹션의 인덱스
layoutEnvironment: 현재 레이아웃의 환경을 나타내는 객체 (디바이스의 방향, 크기 등의 추가 정보)

클로저 내부에서는 이 매개변수들을 사용하여 조건에 따라 적절한 NSCollectionLayoutSection 객체를 생성하고 반환한다.
이 방식은 매우 유연하며, 런타임에 따라 레이아웃을 조정해야 하는 복잡한 UI를 구현할 때 유용하다.

// 단일 섹션 레이이아웃 생성
init(section: NSCollectionLayoutSection)
let layout = UICollectionViewCompositionalLayout(section: section)

// 섹션 프로바이더를 사용한 레이아웃 생성
init(sectionProvider: @escaping SectionProvider)
return layout


CollectionView의 요소

item Cell: 메인 데이터를 표시하는데 사용하는 UI
supplementaryView: 메인 데이터를 보충해주는 UI (헤더나 푸터),
한 섹션을 기준으로 9가지의 방향으로 표현이 가능하다.

decorationView: 데이터를 표출하지 않고 단순히 레이아웃을 꾸미는데 사용되는 UI (컬렉션뷰의 모델과 독립적인 뷰, 배경, 섹션 하이라이트 등)



SupplementaryView 사용하기

먼저 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 사용하기

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


참고: 참고

0개의 댓글