본문 바로가기

iOS

[iOS] Compositional Layout 활용 예시

Compositional Layout

 

안녕하세요 :D

 

이번에 사이드프로젝트를 진행하면서 보다 복잡한 레이아웃을 구현하게 되어 Compositional Layout을 사용하게 되었어요.

 

간만에 사용을 하려다보니 이왕 하는것 다시 공부하며 정리한 내용을 포스팅 하려고 해요.

 

Compositional Layout 이란?


Apple Developer 공식 문서에서는 UICollectionViewCompositionalLayout 을 아래와 같이 소개하고 있어요.

UICollectionViewCompositionalLayout

A layout object that lets you combine items in highly adaptive and flexivle visual arrangements.

 

적응력이 뛰어나고 유연한 시각적인 배열들을 결합할 수 있는 레이아웃 객체라고 합니다.

 

CompositionalLayout 은 CollectionView 의 layout의 종류 중 하나에요.

 

각각의 작은 구성 요소들을 결합 또는 합성하여 컨텐츠의 대한 다양한 시각적인 배열을 제공할 수 있도록 설계되어 있어요.

 

Compositional Layout 구성

 

CompositionalLayout 은 각각의 시각적 그룹들을 가지는 섹션들로 구성되며 하나 이상의 섹션들을 기반으로 보다 유연하고 커스텀에 유용한 레이아웃을 만들 수 있도록 제공해요.

 

각각의 섹션들은 그룹으로 구성되며 그룹은 아이템으로 구성이 되요.

 

 

어떻게 사용하는가?


Compositional Layout 의 객체를 생성할때는 section 또는 sectionProivder를 파라미터로 전달받아요.

section 객체를 전달받는 생성자
sectionProvider 클로저를 통한 생성자

SectionProvider 클로저를 활용한 생성자를 사용할 경우, 각 섹션마다 다양한 레이아웃을 정의할 수 있어요.

 

위에서 얘기했듯 item들의 구성으로 Group을 만들고 Group을 구성하여 Section을 만들고 Section의 구성으로 Layout이 생성되요.

 

각 요소들을 어떻게 생성하는지 알아보기 이전에 Size지정 방식에 대해 먼저 알아볼게요.

 

Size

NSCollectionLayoutDimension

 

각 요소들의 size를 지정해주는 방법은 크게 3가지가 존재해요.

  • fractionalWidth & fractionalHeight: 컨테이너와의 너비 혹은 높이의 비율
  • absolute: 절대값 포인트로 지정
  • estimated 후에 content의 크기가 바뀌어 크기가 정확하지 않을때는 estimated한 값을 사용

 

위와 같이 3개 방식을 이용하여 아래처럼 정의해요.

 

let itemSize = NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(0.2),      // 컨테이너 너비의 0.2배
    heightDimension: .fractionalHeight(1.0)     // 컨테이너 높이와 동일한 높이
)

let item = NSCollectionLayoutItem(itemSize: itemSize)

 

group과 section 또한 모두 동일한 방식으로 Size를 지정해서 사용해요.

 

 

Group & Section

NSCollectionLayoutGroup

 

Group은 크게 3가지 형태로 제공해요.

  • horizontal: 아이템들을 수평 형태로 그룹핑해요
  • vertical: 아이템들을 수직 형태로 그룹핑해요
  • custom: 아이템의 absolute 크기와 위치를 각각 지정해요

 

NSCollectionLayoutSection

Section을 생성할때는 Group을 파라미터로 받아서 사용해요.

 

 

Example


컴포지셔널 예시 이미지

Example

 

위 예시와 같은 레이아웃을 만들어 볼게요!

 

예시에는 크게 3가지 스타일이 존재해요.

  1. Full 사이즈 메인 아이템 하나의 구성
  2. 메인 아이템과 작은 아이템 2개로 조합된 구성
  3. 작은 아이템 3개로 조합된 구성

1번 아이템은 매우 간단해요.

 

별도로 아이템들을 통해 구성한 그룹을 만들 필요가 없이 하나의 큰 아이템이면 충분합니다!

 

// 1번: 큰 아이템 1개
let fullPhotoItem = NSCollectionLayoutItem(
    layoutSize: NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalWidth(2/3)
    )
)

 

2번의 경우에는 큰 아이템 하나와 수직으로 배치된 작은 아이템 2개가 수평으로 배치되어있어요.

 

이를 Group으로 표현해내기 위해서는 여러 단계가 필요해요.

  • 각각의 아이템 정의하기.
  • 작은 아이템 2개 수직으로 배치하기.
  • 큰 아이템과 2개의 수직 아이테을 수직 배치한 그룹을 그룹핑하기.
// 2번: 큰 아이템 1개 + 작은 아이템 2개
let mainItem = NSCollectionLayoutItem(
    layoutSize: NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(2/3),
        heightDimension: .fractionalHeight(1.0)
    )
)

let pairItem = NSCollectionLayoutItem(
    layoutSize: NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalHeight(0.5)
    )
)

// 2개의 작은 아이템을 수직 배치.
let trailingGroup = NSCollectionLayoutGroup.vertical(
    layoutSize: NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1/3),
        heightDimension: .fractionalHeight(1.0)),
        subitem: pairItem, count: 2
    )

// 1개의 큰 아이템과 작은 아이템 2개를 수직 배치한 그룹을 그룹핑
let mainWithPairGroup = NSCollectionLayoutGroup.horizontal(
    layoutSize: NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1.0),
        heightDimension: .fractionalWidth(4/9)),
        subitems: [mainItem, trailingGroup])

 

3번 아이템도 매우 간단해요!

 

아래와 같이 작은 아이템 3개를 horizitonal Group으로 제공하면 됩니다.

 

let tripletItem = NSCollectionLayoutItem(
  layoutSize: NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1/3),
    heightDimension: .fractionalHeight(1.0))
)

let tripletGroup = NSCollectionLayoutGroup.horizontal(
  layoutSize: NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .fractionalWidth(2/9)),
    subitems: [tripletItem, tripletItem, tripletItem]
)

 

마지막으로 2번 그룹을 좌우 대칭시킨, 큰 아이템이 오른쪽에 위치하는 형태의 그룹은 아래와 같이 표현해요.

 

let mainWithPairReversedGroup = NSCollectionLayoutGroup.horizontal(
  layoutSize: NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .fractionalWidth(4/9)),
  subitems: [trailingGroup, mainItem])

 

그리고 여태 만든 모든 그룹들을 하나의 그룹으로 묶어서 Compositional Layout 객체를 만들어볼게요.

 

let nestedGroup = NSCollectionLayoutGroup.vertical(
  layoutSize: NSCollectionLayoutSize(
    widthDimension: .fractionalWidth(1.0),
    heightDimension: .fractionalWidth(16/9)),       // 이와 같이 width의 비율이 1.0을 넘어갈수도 있어요!
  subitems: [fullPhotoItem, mainWithPairGroup, tripletGroup, mainWithPairReversedGroup]
)

let section = NSCollectionLayoutSection(group: nestedGroup)
let layout = UICollectionViewCompositionalLayout(section: section)

 

그러면 위에서 봤던 예시 이미지와 매우 유사한 레이아웃이 보여지게 됩니다!

 

저는 이 Compositional Layout을 사이드 프로젝트의 검색 기능에 활용했어요!

Phochak - Search
1번 그룹

1번 그룹은 왼쪽에 큰 영상과 오른쪽에 수직으로 2개의 영상을 수평으로 묶어서 제공하고

 

2,3번 그룹

2번과 3번 그룹은 수평으로 3개의 영상을 묶어서 제공했어요!

 

최종 결과 영상은 아래와 같아요!

Phochak - Search

Section에 따른 다양한 레이아웃을 제공하거나 여러 레이아웃을 조합한 섹션을 제공할 때 Compositional Layout을 활용하면 더욱 간결하고 명확한 코드로 멋진 레이아웃을 제공하는데 큰 도움이 될 것 같아요 :D

 

 

이상으로 오늘의 포스트는 여기서 이만 마치도록 할게요!

혹시나 궁금하신 내용 혹은 틀린 내용이 있다면 반드시 무적권 댓글로 남겨주세요 :D

 

Reference


'iOS' 카테고리의 다른 글

[iOS] @inlinable  (0) 2023.08.08
[iOS] Swift Concurrency  (0) 2023.08.05
[iOS] Singleton 사용시 주의점과 의존성 주입  (0) 2023.05.24
[iOS] Protocol Composition과 Default Implementation 활용하기  (0) 2023.05.22
[iOS] RunLoop  (0) 2023.05.20