UICollectionViewCompositionalLayout은 iOS에서 컬렉션 뷰 레이아웃을 정의하는 데 사용되는 강력하고 유연한 레이아웃 시스템입니다.
이 레이아웃은 컴포지셔널 디자인의 개념을 도입하여 섹션, 그룹, 항목을 계층적으로 정의해 복잡한 레이아웃을 간단하게 구성할 수 있도록 해줍니다.
구성 요소
1. Items (항목)
- 컬렉션 뷰에서 가장 작은 데이터 단위입니다.
- NSCollectionLayoutItem 클래스를 사용해 정의합니다.
- 크기(size)와 경계(insets)를 설정할 수 있습니다.
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
2. Groups (그룹)
- 여러 항목을 포함하는 컨테이너입니다.
- NSCollectionLayoutGroup 클래스를 사용해 정의하며, 그룹 내의 항목 배치를 설정할 수 있습니다.
- 그룹의 방향(horizontal, vertical)과 크기를 정의합니다.
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(0.2))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
3. Sections (섹션)
- 그룹을 포함하며 컬렉션 뷰의 한 영역을 나타냅니다.
- NSCollectionLayoutSection 클래스를 사용해 정의합니다.
- 섹션 간격, 콘텐츠 방향, 헤더/푸터 설정 등을 추가할 수 있습니다.
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 10
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
4. Layout (레이아웃)
- 섹션으로 구성된 전체 컬렉션 뷰의 레이아웃입니다.
- UICollectionViewCompositionalLayout를 사용하여 생성합니다.
let layout = UICollectionViewCompositionalLayout(section: section)
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
전체 흐름 요약
- Item → Group → Section → Layout의 순서로 설계합니다.
- 설계한 Layout을 UICollectionView에 연결합니다.
- 데이터 소스(UICollectionViewDataSource)를 통해 데이터를 공급합니다.
전체 예시
import UIKit
class ViewController: UIViewController, UICollectionViewDataSource {
lazy var collectionView: UICollectionView = {
// 1. Item 정의
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
heightDimension: .estimated(50))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)
// 2. Group 정의
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(100))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item, item])
// 3. Section 정의
let section = NSCollectionLayoutSection(group: group)
section.interGroupSpacing = 10
section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
// 4. Layout 정의
let layout = UICollectionViewCompositionalLayout(section: section)
// 5. UICollectionView 생성
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.backgroundColor = .white
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
return collectionView
}()
let data = Array(1...20).map { "Item \($0)\\nDynamic content for size adjustment" }
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
}
// MARK: - UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
cell.backgroundColor = .systemBlue
// 셀 텍스트 레이블 추가
let label = UILabel(frame: cell.contentView.bounds)
label.text = data[indexPath.item]
label.numberOfLines = 0
label.textAlignment = .center
label.textColor = .white
label.font = UIFont.boldSystemFont(ofSize: 16)
label.sizeToFit()
// 중복 방지
for subview in cell.contentView.subviews {
subview.removeFromSuperview()
}
cell.contentView.addSubview(label)
return cell
}
}
🔥 NSCollectionLayoutSize는 UICollectionViewCompositionalLayout에서 아이템, 그룹, 또는 섹션의 크기를 정의할 때 사용되는 클래스입니다. 이 클래스는 세 가지 방식으로 크기를 설정할 수 있습니다:
1. 절대 값 (Absolute)
- 설명: 크기를 고정된 점(point) 값으로 설정합니다. 화면의 크기나 레이아웃에 관계없이, 항상 같은 크기를 유지합니다.
- 사용 예시: 아이콘, 버튼 등 항상 고정된 크기를 가져야 하는 경우.
let absoluteSize = NSCollectionLayoutSize(widthDimension: .absolute(44),
heightDimension: .absolute(44))
2. 추정 값 (Estimated)
- 설명: 초기 추정 크기를 제공하며, 시스템이 런타임에 실제 콘텐츠 크기에 맞게 자동으로 크기를 계산합니다.
- 사용 예시: 데이터에 따라 크기가 유동적으로 변하는 콘텐츠 (예: 동적으로 늘어나는 텍스트).
let estimatedSize = NSCollectionLayoutSize(widthDimension: .estimated(200),
heightDimension: .estimated(100))
3. 비율 값 (Fractional)
- 설명: 부모 컨테이너의 크기에 대해 상대적인 비율로 크기를 설정합니다.
- 사용 예시: 특정 비율을 유지해야 하는 콘텐츠 (예: 화면 너비의 50%에 해당하는 셀).
let fractionalSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
heightDimension: .fractionalWidth(0.2))
🔥 Estimated? 이건 꼭 필요한가?
- 초기 레이아웃 성능 최적화:
- UICollectionView는 처음 레이아웃을 계산할 때 .estimated 값을 기준으로 셀의 위치와 크기를 미리 계산합니다.
- 이 값이 너무 작거나 크면 레이아웃 계산이 비효율적일 수 있습니다.
- 스크롤 성능:
- 스크롤 시 새로운 콘텐츠를 로드하거나 셀이 재사용될 때 .estimated 값이 초기 레이아웃의 크기를 결정합니다.
- 현실적인 추정값을 제공하면, 콘텐츠를 올바르게 예측하여 불필요한 레이아웃 재계산을 줄일 수 있습니다.
- 플리커 현상 방지:
- .estimated 값이 너무 작으면 콘텐츠 크기가 변경될 때 플리커(깜빡임) 현상이 발생할 수 있습니다.
- 적절한 추정값을 제공하면 이러한 문제를 최소화할 수 있습니다.
결론 -> 적절하게 사용할 것
- .estimated 값은 초기 레이아웃 계산과 성능 최적화를 위해 중요합니다.
- 현실적인 평균값을 설정하는 것이 가장 이상적입니다.
- 예: 콘텐츠 크기가 100~200 사이면, 추정값은 150 정도로 설정.
- 너무 작거나 큰 값을 사용하면 플리커 현상, 스크롤 성능 저하 등의 문제가 발생할 수 있습니다.
'UIKIT' 카테고리의 다른 글
setContentHuggingPriority, setContentCompressionResistancePriority (0) | 2024.12.17 |
---|---|
UICollectionViewCompositionalLayout - 섹션 헤더 (0) | 2024.12.11 |
UIImageView를 customImageView 생성 (2) | 2024.12.04 |
상대적 경로(Relative Path)와 절대적 경로(Absolute Path)의 차이 (0) | 2024.11.25 |
이미지 선택한 갯수 표시 및 최대치 도달시 알림창 띄우기 (0) | 2024.11.25 |