CollectionView의 Section
CollectionView는 여러 Section을 둘 수 있다. 가장 대표적인 예로는 App Store이다. 아래 사진을 보면 아래로도 스크롤이 되며 여러 단락으로 내용이 나뉘어져 있고 각 단락이 가로로 스크롤이 되어 여러 아이템들을 보여준다.
앱스토어처럼 항상 collectionView의 데이터가 있는 경우도 있지만 메모, 다이어리를 저장 목적 CollectionView는 데이터 저장 전이나 데이터를 다 삭제한 경우에는 CollectionView가 데이터 없이 빈 공간으로 있어야 한다. Section이 하나거나 마지막 Section이라면 데이터가 없더라도 문제되지 않지만 첫 번째 또는 중간 Section이라면 데이터가 없을 경우 다음 Section까지 그저 텅 빈 공간으로 되어 있어 사용자 경험이 좋지 않다.
Section 숨기기 - CompositionalLayout
그렇다면 이 텅 빈 공간을 어떻게 처리해야 할까? 나는 Section을 숨김으로써 해결했다. 정확하게는 해당 section의 높이를 0으로 만들어주어 숨긴 것처럼 표현했다. 이러한 방법을 사용한 이유는 아래 글에 시행착오와 함께 적어두었다.
Section을 여러 개 두어 복잡한 CollectionView를 만들 때에는 FlowLayout보다 Compositional Layout이 더 낫다. Compositional이 '구성의'라는 뜻을 가진 것처럼 Compositional Layout은 아이템, 그룹, 섹션, 레이아웃 각각을 구성하여 만들어지기 때문에 Section 별로 따로 Layout을 구성할 수 있어 코드 가독성이 좋다. 나는 각각을 구성한다 점을 활용하여 SectionLayout을 Empty 버전과 Data 버전 두 개를 만들었다.
//데이터가 있는 경우
func creatDataSectionLayout() -> NSCollectionLayoutSection {
//size
let sizeItem = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1))
let sizeGroup = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.9), heightDimension: .estimated(50))
//item
let item = NSCollectionLayoutItem(layoutSize: sizeItem)
//group
let group = NSCollectionLayoutGroup.horizontal(layoutSize: sizeGroup, subitems: [item])
group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 10)
//section
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 10, trailing: 10)
section.orthogonalScrollingBehavior = .groupPaging
return section
}
//데이터가 없는 경우
func createEmptySectionLayout() -> NSCollectionLayoutSection {
//size
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(0.0))
//item
let item = NSCollectionLayoutItem(layoutSize: size)
//group
let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item])
//section
let section = NSCollectionLayoutSection(group: group)
return section
}
그리고 이 SectionLayout들을 데이터 유무에 따라 Layout에 할당해 Layout 객체를 만들었다.
struct ContentsCollectionItemModel {
var items: [Any]?
}
...
let layout = self.layout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
...
func layout() -> UICollectionViewCompositionalLayout {
let layout = UICollectionViewCompositionalLayout {[weak self] (sectionNumber, _) -> NSCollectionLayoutSection? in
guard let self = self else { return nil }
let sectionIsEmpty = self.contentsCollectionItems[sectionNumber].items?.isEmpty ?? true
if sectionIsEmpty {
return self.createEmptySectionLayout()
}
else {
return self.createDraftSectionLayout()
}
}
return layout
}
CollectionView의 Layout 설정
CollectionView는 Layout을 아이템 삭제나 추가 등 collction View에 변경사항이 생길 때마다 레이아웃이 설정된다. 따라서 임의로 레이아웃을 재설정해주는 작업을 하지 않아도 데이터가 없거나 삭제된 직후 데이터 유무에 따라 Layout이 구분되어 설정된다.
UICollectionViewLayout
collection view는 collection view의 레이아웃 객체에 여러 번 collection view 요소에 대한 레이아웃 정보를 제공하도록 요청한다. ... 유사하게 아이템이 collection view에 추가되거나 삭제될 때마다 해당 아이템들에 대해 추가적인 레이아웃 전달이 발생한다.
...
collection view에 있는 데이터가 변하고 아이템이 추가되거나 삭제될 때, collection view은 collection view의 레이아웃 객체가 레이아웃 정보를 업데이트하도록 요청한다. 특히 옮겨지거나 추가, 삭제된 모든 아이템들은 새로운 위치를 반영한 업데이트된 collection view의 레이아웃 정보를 가져야 한다.
결과
'iOS' 카테고리의 다른 글
[ iOS ] Bounds와 Frame 비교 - 1. 공식문서와 origin (0) | 2023.06.30 |
---|---|
[ iOS ] 동기와 비동기 / 직렬과 동시성 (0) | 2023.06.22 |
[ iOS ] ScrollView 화면 캡쳐하기 (Image Creation) (0) | 2023.02.18 |
[ iOS ] UserDefaults - 커스텀 타입 저장하기 (Attempt to insert non-property list object 오류) (0) | 2023.01.21 |
[ iOS ] Property list (0) | 2023.01.20 |