UICollectionView CellRegistration — 옛날 register/dequeue 그만 쓰자
iOS 14에서 들어온 UICollectionView.CellRegistration. 문자열 reuse ID 안 만들어도 되고, as! 캐스팅도 사라지고, cell 등록과 설정 코드가 한 곳으로 모인다. 단독으로도 쓸 수 있다.
MAY 12, 2026 · 3 MIN READ#uikit#uicollectionview#cellregistration#ios14
iOS 14에서 들어온 API인데 의외로 잘 안 알려진 쪽. 옛날 register(_:forCellWithReuseIdentifier:) + dequeueReusableCell(withReuseIdentifier:) 패턴을 쓰지 않게 된다. 처음 보면 살짝 낯설 수 있는데, 한 번 잡아두면 옛날 패턴으로 안 돌아감.
DiffableDataSource 자료에 같이 묶여 나오는 경우가 많아서 “DiffableDataSource 쓸 때만 쓰는 거” 같이 보이는데, 사실 단독으로도 그냥 쓸 수 있다. 옛날 UICollectionViewDataSource 그대로 가면서 cell 등록만 갈아도 ID 관리가 사라져서 깔끔해짐.
옛날 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
| // viewDidLoad 어딘가
collectionView.register(CustomCell.self, forCellWithReuseIdentifier: "cell")
// cellForItemAt
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: "cell",
for: indexPath
) as! CustomCell
cell.configure(with: items[indexPath.item])
return cell
}
|
세 가지가 분산돼 있다.
- cell 클래스 등록 (viewDidLoad)
- dequeue 후 강제 캐스팅 (
as! CustomCell) - cell 설정 (configure 호출)
ID 문자열이 두 곳에 흩어져 있고, 캐스팅을 빼먹으면 컴파일은 되는데 cell 타입을 알 수 없어 메서드 호출이 안 된다.
CellRegistration 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
| let cellRegistration = UICollectionView.CellRegistration<CustomCell, Item> { cell, indexPath, item in
cell.configure(with: item)
}
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let item = items[indexPath.item]
return collectionView.dequeueConfiguredReusableCell(
using: cellRegistration,
for: indexPath,
item: item
)
}
|
세 가지가 한 덩어리로 모인다.
- cell 클래스 + 구성 클로저가
CellRegistration 인스턴스 안에 같이 묶여 있음 - 제네릭(
<CustomCell, Item>)으로 cell 타입과 item 타입이 둘 다 잡힘. as! 캐스팅 사라짐 - 문자열 reuse ID 안 만들어도 됨. registration 인스턴스 자체가 ID 역할
cellRegistration은 보통 ViewController의 lazy property로 들고 있으면 됨. lazy로 하면 viewDidLoad에 등록 코드 따로 안 박아도 처음 dequeue 시점에 자동으로 등록됨.
1
2
3
| private lazy var cellRegistration = UICollectionView.CellRegistration<CustomCell, Item> { cell, _, item in
cell.configure(with: item)
}
|
DiffableDataSource랑 단독 사용
DiffableDataSource랑 같이 쓰면 자연스럽게 한 줄로 묶인다.
1
2
3
4
5
6
7
8
9
| let dataSource = UICollectionViewDiffableDataSource<Section, Item>(
collectionView: collectionView
) { collectionView, indexPath, item in
collectionView.dequeueConfiguredReusableCell(
using: cellRegistration,
for: indexPath,
item: item
)
}
|
numberOfItems/cellForItemAt 자체를 안 짜도 된다. DiffableDataSource가 데이터를, CellRegistration이 cell 구성을 담당.
옛날 UICollectionViewDataSource를 그대로 쓰면서 CellRegistration만 도입해도 됨. cell 등록·캐스팅·ID 관리만 빠져도 화면 코드가 한참 짧아짐.
SupplementaryRegistration도 있다
header/footer 같은 supplementary view용으로 UICollectionView.SupplementaryRegistration이 따로 있다. 사용 패턴은 거의 동일.
1
2
3
4
5
6
7
8
9
10
11
12
| let headerRegistration = UICollectionView.SupplementaryRegistration<HeaderView>(
elementKind: UICollectionView.elementKindSectionHeader
) { header, kind, indexPath in
header.titleLabel.text = "Section Header"
}
dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
collectionView.dequeueConfiguredReusableSupplementary(
using: headerRegistration,
for: indexPath
)
}
|
흔한 사고 — registration 매번 새로 만들기
1
2
3
4
5
6
7
8
| // ❌ cellForItemAt 안에서 매번 새로 만들면 안 됨
func collectionView(_ collectionView: UICollectionView,
cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cellRegistration = UICollectionView.CellRegistration<CustomCell, Item> { ... }
return collectionView.dequeueConfiguredReusableCell(
using: cellRegistration, for: indexPath, item: items[indexPath.item]
)
}
|
CellRegistration은 만드는 시점에 cell 클래스를 collection view에 등록한다. 매번 새로 만들면 등록이 매번 일어나서 cell 재사용 풀이 꼬임. lazy property나 ViewController 프로퍼티로 한 번 만들고 재사용해야 함.
UIKit minimum 버전이 14+면 새 코드는 다 이걸로
iOS 14+가 깔린 프로젝트면 새로 짜는 cell 등록 코드는 다 CellRegistration로 가는 게 표준. 옛 패턴 그대로 두는 코드는 그대로 두고, 새로 만지는 부분부터 점진적으로 바꿔도 한 ViewController 안에 두 패턴 섞여있어도 동작에 문제 없음.
처음 보면 “왜 이렇게 한 단계 더 거치지?” 싶은데, 실제로 옛 패턴 vs 새 패턴 cell 코드 양 비교하면 절반 가까이 줄어든다. 캐스팅 사라지는 게 진짜 큼.