UICollectionReusableView와 UIView의 차이
UICollectionReusableView
- UICollectionView에서 재사용이 가능한 뷰를 위한 클래스입니다.
- 주로 섹션 헤더, 섹션 푸터, 또는 커스텀 장식을 위한 뷰를 만들 때 사용됩니다.
- 재사용 큐(reuse queue)를 활용하여 메모리 효율성을 극대화합니다.
- UICollectionReusableView는 UICollectionView와 연동하여 동작하도록 설계되어 있습니다.
- 예를 들어, 재사용 식별자(reuseIdentifier)를 통해 등록하고 dequeuing하는 메서드가 제공됩니다.
UIView
- iOS에서 기본적으로 사용되는 뷰 클래스입니다.
- 재사용 메커니즘이 없으므로, 메모리 관리와 재사용은 개발자가 직접 처리해야 합니다.
- 기본적으로 뷰 자체를 재사용하는 컬렉션 뷰의 요구 사항을 만족시키지 못합니다.
등록 및 dequeue 메커니즘
- UICollectionReusableView는 재사용 식별자(reuseIdentifier)를 통해 UICollectionView와 등록 및 dequeuing 작업을 수행합니다.
collectionView.register(
MyHeaderView.self,
forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: "header")
func collectionView(_ collectionView: UICollectionView,
viewForSupplementaryElementOfKind kind: String,
at indexPath: IndexPath) -> UICollectionReusableView {
let header = collectionView.dequeueReusableSupplementaryView(
ofKind: UICollectionView.elementKindSectionHeader,
withReuseIdentifier: "header",
for: indexPath
) as! MyHeaderView
header.configure(with: data) // 헤더 구성
return header
}
- UICollectionReusableView는 UICollectionViewCompositionalLayout에서도 섹션 헤더/푸터 역할에 완벽히 맞춰 작동합니다.
- 헤더를 정의할 때, 다음처럼 NSCollectionLayoutBoundarySupplementaryItem에 등록합니다:
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(50))
let header = NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: headerSize,
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
)
- 섹션 헤더/푸터로는 반드시 UICollectionReusableView를 사용해야 합니다.
- 이유:
- 재사용 메커니즘: 메모리 효율성 극대화.
- 데이터와 연동: 데이터 소스와 잘 통합되어 데이터 설정이 용이.
- 레이아웃 통합: UICollectionViewCompositionalLayout과 완벽히 호환.
- 기능 최적화: 헤더/푸터의 역할에 맞는 메서드와 구조 제공.
- UIView는 이러한 작업에 적합하지 않으며, 섹션 헤더/푸터의 특성상 재사용이 중요하기 때문에 반드시 UICollectionReusableView를 사용해야 합니다.
func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<TMDBData, MainResults>(collectionView: collectionView) {
collectionView, indexPath, model in
switch self.combineSection.combineTMDB[indexPath.section].type {
case .topRatedMovie:
return self.configure(TopRatedCell.self, with: model, for: indexPath)
default:
return self.configure(TodayCollectionViewCell.self, with: model, for: indexPath)
}
}
dataSource?.supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in
guard let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: SectionHeader.reuseIdentifier, for: indexPath) as? SectionHeader else { return nil}
guard let firstApp = self?.dataSource?.itemIdentifier(for: indexPath) else { return nil }
guard let section = self?.dataSource?.snapshot().sectionIdentifier(containingItem: firstApp) else { return nil }
switch section.type {
case .noewPlayingMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "상영중인 작품"
case .popularMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "선호하는 작품"
case .topRatedMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "작품 순위"
case .upcomingMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "개봉 예정 작품"
default:
return nil
}
return sectionHeader
}
}
위의 함수에서 각 sectionHeader를 설정하는 부분은 UICollectionView의 섹션 헤더를 구성하기 위한 로직입니다. 헤더 뷰는 각 섹션에 대한 제목 및 부제목과 같은 추가 정보를 표시하기 위해 사용됩니다. 이 과정은 Diffable Data Source의 supplementaryViewProvider를 통해 구현되었습니다.
supplementaryViewProvider 등록
dataSource?.supplementaryViewProvider = { [weak self] collectionView, kind, indexPath in
...
}
- supplementaryViewProvider는 섹션 헤더 또는 푸터와 같은 추가 뷰를 생성하고 반환하는 클로저입니다.
- collectionView: 헤더나 푸터가 표시될 컬렉션 뷰.
- kind: Supplementary View의 종류. 보통 UICollectionView.elementKindSectionHeader로 섹션 헤더를 나타냅니다.
- indexPath: 헤더가 배치될 섹션의 IndexPath.
헤더 뷰 재사용
guard let sectionHeader = collectionView.dequeueReusableSupplementaryView(
ofKind: kind,
withReuseIdentifier: SectionHeader.reuseIdentifier,
for: indexPath
) as? SectionHeader else { return nil }
- dequeueReusableSupplementaryView: 재사용 가능한 헤더 뷰를 큐에서 가져옵니다.
- SectionHeader.reuseIdentifier: 헤더 뷰의 재사용 식별자입니다. SectionHeader는 UICollectionReusableView를 상속한 클래스입니다.
- 가져온 뷰를 SectionHeader로 다운캐스팅합니다.
현재 섹션 데이터를 가져오기
guard let firstApp = self?.dataSource?.itemIdentifier(for: indexPath) else { return nil }
guard let section = self?.dataSource?.snapshot().sectionIdentifier(containingItem: firstApp) else { return nil }
- itemIdentifier(for:): 현재 indexPath에 해당하는 아이템(셀)을 가져옵니다. 이는 MainResults 타입의 데이터를 반환합니다.
- sectionIdentifier(containingItem:): 위에서 가져온 MainResults가 속한 섹션(TMDBData)을 가져옵니다.
- 결과적으로, section에는 현재 섹션(TMDBData)이 담기며, 이를 통해 해당 섹션의 type에 접근할 수 있습니다.
헤더 뷰 설정
switch section.type {
case .nowPlayingMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "상영중인 작품"
case .popularMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "선호하는 작품"
case .topRatedMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "작품 순위"
case .upcomingMovie:
sectionHeader.title.text = section.type?.rawValue
sectionHeader.subTitle.text = "개봉 예정 작품"
default:
return nil
}
- section.type: 현재 섹션의 타입입니다. 이는 API 호출 시 설정된 SectionType 열거형 값을 나타냅니다.
- 각 타입에 따라 헤더 제목(title)과 부제목(subTitle)을 설정합니다.
- rawValue: 열거형 타입의 문자열 값으로, 섹션의 이름을 표시하는 데 사용됩니다.
- subTitle: 섹션에 대한 추가 설명입니다.
- 예를 들어, 타입이 .topRatedMovie라면 헤더는 다음과 같이 설정됩니다:
- 제목: "Top Rated Movie" (rawValue)
- 부제목: "작품 순위"
'UIKIT' 카테고리의 다른 글
ViewModel을 사용하는 목적 (0) | 2025.01.08 |
---|---|
UITabBarAppearance (0) | 2025.01.05 |
UIFontMetrics (0) | 2025.01.05 |
키보드 내리기 (0) | 2024.12.27 |
UICollectionView의 셀 크기를 동적으로 정사각형으로 조정하려면? (0) | 2024.12.22 |