프로젝트를 진행하던 중 UICollectionView의 Compositional Layout을 사용해 3x3 행렬의 layout을 가지고 9개 이상의 item이 존재한다면 수평스크롤을 통해 다음페이지에 cell을 생성하는 레이아웃을 생성하였습니다.
이때, item의 개수가 0~6개 일때도 설정해둔 fractionalHeight(0.675)로 인해 빈공간이 생기고 0.675의 비율을 넘어서는 공간에 다음섹션이 존재하는 부자연스러운 레이아웃을 생성하여 item의 개수에 따라 해당 섹션의 높이를 지정해주고자 하는데에서 공부하게 되었습니다.
이전의 코드는 다음과 같습니다.
private func brandsSection() -> NSCollectionLayoutSection {
// item
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0 / 3.0),
heightDimension: .estimated(150)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: 15, leading: 15, bottom: 15, trailing: 0)
// group
let lineGroupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(100)
)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.92),
heightDimension: .fractionalHeight(0.675) //화면의 0.675만큼을 차지하는 고정적인 넓이
)
let lineGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: lineGroupSize,
subitem: item,
count: 3
)
let group = NSCollectionLayoutGroup.vertical(
layoutSize: groupSize,
subitems: [lineGroup]
)
//header
let headerSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.9),
heightDimension: .absolute(40)
)
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
// section
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPaging
section.boundarySupplementaryItems = [header]
return section
}
- UICollectionViewDelegateFlowLayout 활용
먼저 UICollectionView의 section높이를 동적으로 지정하기 위해 UICollectionViewDelegateFlowLayout 프로토콜을 활용하였으나,
작동하지 않았고 예상되는 이유는 다음과 같았습니다.
- Compositional Layout은 Collection View의 레이아웃을 관리하는 데에 특화된 독립적인 시스템이다.
Compositional Layout은 독립적인시스템이기 때문에 UICollectionViewDelegateFlowLayout 프로토콜을 활용하기 위해서는 추가적인 작업이 필요로 했고, 조금 더 직접적인 방법을 고민했습니다.
- 연산프로퍼티(Computed Property) 활용
섹션에 직접적인 높이를 다루는 필드는 brandSection 메소드이므로, 여기서 높이를 동적으로 변경하여야겠다고 생각했고
item의 개수에따라 높이를 반환하는 변수를 생성했습니다.
또한 item의 크기를 부분적(fractional)으로 준다면 섹션의 크기가 줄어들면서 item의 크기도 줄어들기 때문에 estimated를 활용해 아이템의 크기를 지정했습니다.
(estimated - estimated는 예상치를 사용하여 크기를 결정하는 옵션입니다. 이 옵션은 해당 dimension의 크기를 대략적으로 추정하고, 컨텐츠의 실제 크기에 따라 동적으로 조정될 수 있습니다. estimated 크기는 컨텐츠의 크기나 내용에 따라 유동적으로 변경될 수 있으며, 컨텐츠 크기를 정확히 알 수 없는 경우 유용합니다.)
private var fractionalHeight: Float {
get {
switch self.brandList.count {
case 0...3:
return 0.225
case 4...6:
return 0.45
default:
return 0.675
}
}
}
.
.
.
.
private func brandsSection() -> NSCollectionLayoutSection {
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0 / 3.0),
heightDimension: .estimated(150)
)
.
.
.
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(0.92),
heightDimension: .fractionalHeight(CGFloat(fractionalHeight))
)
다음과 같이 아이템의 개수에 따라 item의 개수에 따라 section의 높이가 동적으로 변경되었지만 UI적인 측면에서 animation없이 순간적인 새로고침현상처럼 layout이 변경되어 animation을 주고자 했습니다.
- collectionView.performBatchUpdates
- performBatchUpdates는 collectionView의 메소드
- insert, delete, reload, move 연산을 그룹으로 묶어서 일괄적으로 animate
위에서 말했듯이 Compositional Layout은 레이아웃을 관리하는 데에 특화된 독립적인 시스템이기 때문에 이외의
UIViewAnimator나 UIViewPropertyAnimator는 동작하지 않는것 같습니다.
따라서 collectionView의 변화에 대해서 performBatchUpdates를 사용한다면 animation효과를 줄 수 있습니다.
self.collectionView.performBatchUpdates(
{
self.collectionView.reloadSections(NSIndexSet(index: 0) as IndexSet)
}, completion: { (finished:Bool) -> Void in
})
'ios > UI구현' 카테고리의 다른 글
[iOS, Swift] 네이버지도의 BottomSheet (0) | 2023.07.27 |
---|---|
[iOS, Swift] AppStore layout만들어보기(UICollectionView, CompositionalLayout 활용) (0) | 2023.07.27 |
[iOS, Swift] 네이버 웹툰 메인 페이지(UIPageViewController, UISegnmentControl 활용) (0) | 2023.07.27 |
[iOS, Swift] Sticky Header(배달의민족, 당근마켓) (0) | 2023.07.27 |