UICollectionViewDiffableDatasource

*컬렉션뷰 cell 등록 ⇒ 레이아웃 구현 ⇒ datasource (cellProvider) ⇒ snapshot을 만들어서 datasource에 넣어준다

  1. 섹션과 아이템
// 섹션
enum MovieQuery: String, Hashable {
    case now_playing = "상영중인 영화"
    case upcoming = "상영예정인 영화"
}

// 각 섹션 별 아이템 (Result타입의 데이터를 전달)
enum MovieItem: Hashable {
    case oneItemCell(Result)
    case twoItemCell(Result)
}

typealias Section = MovieQuery
typealias Item = MovieItem

// datasource의 섹션과 그 아이템의 타입으로 쓰인다
private var dataSource: **UICollectionViewDiffableDataSource**<Section, Item>?

❖ 레이아웃 구현


  1. 콜렉션뷰 구성
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init())
collectionView.**setCollectionViewLayout**(createCollectionViewLayout(), animated: true)

// 📌 section index별로 UICollectionLayoutSection을 만든다
func createCollectionViewLayout() -> UICollectionViewCompositionalLayout {
	return UICollectionViewCompositionalLayout(**sectionProvider**: { [weak self] **index**, env in
		guard let weakSelf = self else { fatalError() }
		weakSelf.createSection(by: **index**)
	})
}

// 📌 index를 받아서 UICollectionLayoutSection을 만든다
func createSection(by index: Int) -> NSCollectionLayoutSection {
	switch index {
		case 0:
			return createNowSection()
		case 1:
			return createUpcomingSection()
		default: break
	}
}

// 📌 NOW 섹션 (item => group => section)
func createNowSection() -> NSCollectionLayoutSection {
	// supplementaryView (ex. header)
	let titleSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(50))
	let titleSupplementary = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: titleSize, 
																																			 elementKind: .elementKinSectionHeader, // Header
																																			 alignment: .top)
	// item
	let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .fractionalHeight(1.0))
	let item = NSCollectionLayoutItem(layoutSize: itemSize)

	// group
	let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.8), heightDimension: .absolute(450))
	let group = NSCollectionLayoutGroup**.horizontal**(layoutSize: groupSize, subitems: [item]) // 수직 / 수평 방향 지정

	// section
	let section = NSCollectionLayoutSection(group: group)
	section.orthogonalScrollingBehavior = .groupPaging // 스크롤 애니메이션?
	section.interGroupSpaing = 20 // group간격
	section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 35, trailing: 10) // section 패딩
	section.boundarySupplemnetaryItems = [titileSupplemnetary] // 위에 헤더 supplementaryView 추기
}

❖ Datasource (UICollectionViewDiffableDataSource)


// 섹션
enum MovieQuery: String, Hashable {
    case now_playing = "상영중인 영화"
    case upcoming = "상영예정인 영화"
}

// 각 섹션 별 아이템 (Result타입의 데이터를 전달)
enum MovieItem: Hashable {
    case oneItemCell(Result)
    case twoItemCell(Result)
}

typealias Section = MovieQuery
typealias Item = MovieItem

private var dataSource: UICollectionViewDiffableDataSource<Section, Item>?

// ✅ cell 생성
dataSource = **UICollectionViewDiffableDataSource**(collectionView: collectionView, **cellProvider**: {collectionView, indexPath, item in
	switch item {
		case let .oneItemCell(data):
			guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NowPlayingCell.self, 
																												  for: indexPath) as? NowPlayingCell else {return UICollectionViewCell()}
			cell.configure(data)
			return cell
		case let .twoItemCell(data):
			...
	}
})

// ✅ supplementaryView(헤더) 생성
dataSource?.supplementaryViewProvider = { [weak self] (collectionView, kind, indexPath) -> **UICollectionReusableView**? in
	guard kind == UICollectionView.elementKindSectionHeader,
				let weakSelf = self,
				let **header** = collectionView.dequeueReusableSupplementaryView(ofKind: kind, 
																																		 withReuseIdentifier: MovieSectionHeader.identifier,
																																		 for: indexPath) as? MovieSectionHeader else { return nil }

	let section = weakSelf.dataSource?.snapshot().sectionIdentifiers[indexPath.section]
	switch section {
		case .now_playing:
			header.configure(title: "현재 상영중인 영화")
		case .upcoming:
			header.configure(title: "상영 예정인 영화")
		default: break
	}
	return **header**
}

❖ SnapShot


var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()

snapshot.appendSections([Section.now_playing])
snapshot.appendItems(nowMovieItem, toSEction: Section.now_playing)

snapshot.appendSections([Section.upcoming])
snapshot.appendItems(upcomingMovieItem, toSEction: Section.upcoming)

// 📌 snapshot을 datasource에 적용
dataSource?.apply(snapshot)