
안녕하세요 Ruel입니다
오늘은 Compositional Layout에 대해 얘기해보려해요.

이전에 사진과 같은 UI를 만들고 싶었어요.
그래서 저는 UITableView를 만들고 그 안에 UITableViewCell을 넣고
UITableViewCell 안에 UICollectionView를 넣어 리스트 형태에서 해당 섹션에 가로 스크롤이 되는 UI를 구현했었어요.
그런데 ....
UICollectionViewCell에 대한 select Action을 처리하기 위해서
UITableView가 있는 ViewController 까지 두개의 Protocol을 활용하여 터치 이벤트를 처리했어요.
처음에는 "이렇게 하는거군"하고 넘어갔지만
이전 WWDC세션들을 보다가 Compositional Layout을 보게 되었어요.
Compositional Layout를 사용하면 다양한 레이아웃에 대응 할 수 있다라는 것을 보고
바로 리팩토링을 했던 기억이 있습니다.

진즉 알았으면 좋았을것을...그래도 알았으니 된거 아니겠어요?
자주 사용하지 않다보니 까먹을때가 있어서 다시한번 정리해보고자해요!
CompositionalLayout?
기존 UICollectionViewFlowLayout을 사용하여 원하는 UI를 구현할 수 있었지만, 점점 다양한 Layout의 UI가 등장하면서 UI구현에 어려움과 복잡함이 있었는데요.
이를 극복하기 위해서 UICollectionViewCompositionalLayout이 등장했어요!
UICollectionViewCompositionalLayout은 iOS 13이상에서 사용가능한 UICollectionView의 레이아웃을 정의하는 방법이에요.
기존의 UICollectionViewFlowLayout에 비해 훨씬 더 유연하고 강력한 레이아웃의 옵션을 제공해요.
UICollectionViewCompositionalLayout을 사용하면 복잡한 레이아웃을 쉽게 구현할 수 있으며, 다양한 크기와 형태의 셀을 세밀하게 조정할 수 있죠!
CompositionalLayout의 기본 구성 요소
CompositionalLayout을 사용하기 위해서 세가지 주요 요소를 알아야해요

- Item: 레이아웃의 가장 작은 구성요소로, 말 그대로 CollectionView의 cell 하나를 의미해요.
- Group: 여러 개의 Item을 하나의 group으로 묶어주는데, 그룹 내의 아이템이 어떻게 배치될지 결정해주는 역할을 해요.
- Section: 그룹을 묶어 하나의 section을 만들죠. 하나의 섹션에서 다양한 레이아웃을 적용할 수 도 있어요
| Section | Group | Item |
| CollectionView는 여러 Section으로 구성할 수 있고, 각 Section은 독립적으로 Layout을 정의할 수 있어요. |
Section 내에는 하나 이상의 Group이 있고, 이 Group들은 하나의 layout을 공유해요. Group에는 여러 Item이 포함 될 수 있어요. |
>Item은 CollectionView의 Cell을 나타내요. 각 Item은 Group 내에 배치돼요! |
CompositionalLayout의 특징 및 장단점
특징
- 각 Section마다 각각의 Layout을 지정할 수 있어요.
예를 들면, 첫번째 section은 가로 스크롤, 두번째 secion은 격자, 세번째 section은 리스트 등의 형태로 구성할 수 있어요 - Item, Group, Section의 크기를 비율(fractionalWidth, fractionalHeight), 절대 값(absolute), 추정값(estimated) 등으로 지정할 수 있어요. 이것을 사용해서 다양하게 대응이 가능하죠.
- 특정 Section에서만 가로 또는 세로 스크롤을 추가할 수 있어요.
- Section 마다 Header와 Footer를 쉽게 추가할 수 있어요.
- 각 Item 간의 간격, Group 간의 간격등을 세밀하게 조정할 수도 있죠
장점
- Section 별로 다양한 Layout을 지정할 수 있어 하나의 CollectionView에 다양한 UI를 구현할 수 있어요
- Item, Group, Section으로 Layout을 관리할 수 있어요.
- CompositionalLayout은 최적화가 잘되어 있어 대규모 데이터 셋을 효율적으로 처리할 수 있다고 해요.
단점
- 처음 보면 조금 복잡하게 느껴질 수 있어요. Item, Group, Section 세가지를 활용하면서 Layout도 관리해주어야하기 때문이죠.
- 간단한 Layout에서 사용할 경우 불필요하게 복잡해져요.
단순한 UI라면 UICollectionViewFlowLayout으로도 충분히 가능하겠죠 - iOS13버전 이상에서만 사용할 수 있어요. ( 최근에는 대부분 13버전 이상인 것 같아서 크게 문제가 되진 않을거 같네요. )
이처럼 특징, 장단점을 알아보았는데요 이렇게 보면 사실 단점을 장점이 상쇄할 만큼 단점이 크게 느껴지진 않네요.
간단한 UI에서는 불필요하게 사용하지 말자 정도로 머릿속에 기억하면 되겠네요.
Compositional Layout 사용 예제
1. CollectionView Setting
override func viewDidLoad() {
super.viewDidLoad()
// UICollectionViewCompositionalLayout을 사용한 레이아웃 설정
let layout = createCompositionalLayout()
// UICollectionView 초기화
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.backgroundColor = .white
collectionView.delegate = self
collectionView.dataSource = self
// 셀 등록
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
view.addSubview(collectionView)
}
- ViewController에 CompositionalLayout을 사용하여 CollectionView를 만들어줘요.
2. Compositional Layout Setting
func createCompositionalLayout() -> UICollectionViewCompositionalLayout {
return UICollectionViewCompositionalLayout { (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
switch sectionIndex {
case 0:
return self.createFirstSectionLayout()
case 1:
return self.createSecondSectionLayout()
case 2:
return self.createThirdSectionLayout()
default:
return nil
}
}
}
- collectionViewLayout에 넣어줄 layout 생성 메서드를 만들어요
- 여기서 section 별로 각기 다른 메서드를 리턴하고 있는데요. 이렇게 section별로 다양하게 layout을 구성할 수 있어요.
3. 첫번째 Section Layout 구현
func createFirstSectionLayout() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.4), heightDimension: .fractionalHeight(0.2))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuous
return section
}
- Item: 각 Item은 전체 Cell 높이의 100%와 Group 너비의 20%로 설정해주었어요
- Group: 두개의 Item을 포함하는 가로 group으로, group의 크기는 전체 화면 너비의 40%, 높이는 20%로 설정했어요
- Section: 이 section은 가로 스크롤이 가능하도록 설정하였고, 여러 아이템을 한번에 볼 수 있어요
4. 두번째 Section Layout 구현(Grid)
func createSecondSectionLayout() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.33), heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalWidth(0.33))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item, item, item])
let section = NSCollectionLayoutSection(group: group)
return section
}
- Item: 각 Item은 Group의 너비의 33%, group의 전체 높이를 차지해요
- Group: 세개의 Item을 포함하는 가로 group으로, group의 크기는 화면 너비와 동일하고, 높이는 화면 너비의 33%
- Section: 이 section은 격자 형태로 Item을 배치
5. 세번째 Section Layout 구현
func createThirdSectionLayout() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(50))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(50))
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
return section
}
- 이 Layout은 간단한 리스트 형태에요. 각 Item들이 세로로 정렬됩니다.
- Item: 각 Item은 전체 group너비를 차지하고, 고정된 높이 50으로 설정해요.
- Group: 하나의 item을 포함하는 세로 group으로, group의 크기는 item과 동일해요
- Section: 이 section은 간단한 리스트 형태로 item을 배치해요.
6. DataSource 설정
// MARK: - UICollectionViewDelegate, UICollectionViewDataSource
extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 3 // 섹션 수
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 30 // 각 섹션의 아이템 수
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .systemBlue
return cell
}
}
- 현재 3개의 section을 가지고, cell의 개수는 30개로 설정하였어요.
- Cell 디자인은 ..... 이해해주세요..ㅎ
구현 UI 확인

fractional, absolute, estimated
앞서 Layout을 설정하면서 Item에 대한 가로, 세로를 설정해주었을때 보셧을텐데요.
이것들도 한번 얘기해볼 게요
1. 비율(fractionalWidth, fractionalHeight)
- fractional은 부모 요소(group 또는 section)의 크기를 기준으로 상대적인 비율을 설정할 때 사용해요.
- fractionalWidth를 0.5로 설정한다면 해당 item은 부모 group의 너비의 50%가 되죠
- 부모의 요소의 크기에 따라 Item이나 group의 크기가 결정돼요
- 화면 크기에 맞게 자동으로 조정되므로 다양한 디바이스에서 일관적인 비율을 유지할 수 있어요!
2. 절대 값(absolute)
- absolute는 픽셀 단위로 고정된 크기를 설정할 때 사용해요.
- 이 값을 사용하면 Item이나 Group의 크기가 특정 픽셀 값으로 설정되고, 화면크기나 디바이스에 관계없이 고정된 크기를 유지해요
- 크기가 일정해야하는 요소에 적합하게 사용할 수 있겠죠?
3. 추정값(estimated)
- estimated는 Item이나 Group의 크기를 예측할 수 없는 경우, 대략적인 크기를 설정하는데 사용해요.
- 실제 크기는 콘텐츠에 따라 자동으로 조정돼요.(예를 들면 Text의 길이가 가변적인 Cell에서 사용된다고 하네요)
- 콘텐츠에 따라 크기가 조절되다보니 유연한 크기를 갖을 수 있어요 -> 동적인 콘텐츠에 적합하겠죠
| fractional | absolute | estimated |
| 반응형 디자인이 중요할 경우 사용 | 정확한 크기가 필요할 경우 사용 | 콘텐츠의 크기가 유동적일 경우 사용 |
이 세가지를 적절하게 사용하면 원하는 layout의 요구 사항을 가뿐하게 구현할 수 있겠네요!
구현하는 코드와 구현된 UI를 시뮬레이터에서 확인해봤는데요
어떤가요 원하시는 UI로 구현이 가능할 것 같으신가요?
이렇게 보면 TableView보다 상위호환인 것 처럼 느껴져요
리스트도 만들 수 있고 그리드도 만들 수 있고,
허나 리스트만 필요할때는 TableView를 쓰는게 더욱 간단하고 편리할 것 같긴하네요.
UI를 그리는 방법은 너무 다양해서 적절한 선택이 참 중요한것 같아요...

오늘은 여기까지
잘못된 부분 혹은 추가적으로 알려주실 부분 있으시면 언제든지 댓글 남겨주세요.💬
감사합니다.🙇🏻♂️

주말이니까 낮잠 자야징
참고 링크
'iOS > iOS' 카테고리의 다른 글
| Delegate Pattern🤔 (0) | 2024.08.09 |
|---|