Project/MovieClip
❌ 에러 분석 - UICollectionView 2번 dequeue...
밤새는 탐험가89
2025. 2. 23. 15:57
✅ 에러 분석 및 해결 방법
에러 메시지:
Expected dequeued view to be returned to the collection view in preparation for display.
When the collection view's data source is asked to provide a view for a given index path,
ensure that a single view is dequeued and returned to the collection view.
Avoid dequeuing views without a request from the collection view.
🚨 에러 원인:
- UICollectionView는 cell을 dequeue하면 반드시 반환해야 함
- 현재 dequeue한 cell을 configure()에서 반환하지 않고 있음
- configure() 내부에서 새로운 cell을 dequeue하고 있기 때문에 충돌 발생
- 즉, 한 번의 dequeue로 두 개의 cell을 반환하려고 하면서 충돌 발생
✅ 1. 문제 발생 코드 (dequeue를 두 번 실행)
dataSource = UICollectionViewDiffableDataSource<SearchSection, SearchItem>(collectionView: searchResultCollectionView) { searchResultCollectionView, indexPath, item in
// ✅ 여기서 이미 dequeue 실행
guard let cell = searchResultCollectionView.dequeueReusableCell(
withReuseIdentifier: SearchResultCell.reuseIdentifier,
for: indexPath
) as? SearchResultCell else {
return UICollectionViewCell()
}
cell.setViewModel(self.viewModel) // ✅ cell에 viewModel을 설정
switch item {
case .movie(let movie):
return self.configure(SearchResultCell.self, with: .movie(movie), for: indexPath) // ❌ 여기서 또 dequeue
case .tv(let tv):
return self.configure(SearchResultCell.self, with: .tv(tv), for: indexPath) // ❌ 여기서 또 dequeue
case .people(let person):
return self.configure(SearchResultCell.self, with: .people(person), for: indexPath) // ❌ 여기서 또 dequeue
}
}
🚨 문제:
- 이미 dequeue한 cell을 사용해야 하는데,configure() 내부에서 다시 dequeue를 실행해서 충돌 발생
private func configure<T: SelfConfiguringTVCell>(_ cellType: T.Type, with model: TvTMDBResult, for indexPath: IndexPath) -> T {
guard let cell = seriesCollectionView.dequeueReusableCell(withReuseIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else { fatalError("Unable to deque \(cellType)")}
cell.configure(with: model)
return cell
}
✅ 2. 해결 방법: configure() 내부에서 dequeue하지 않도록 수정
🔹 수정 방법
1️⃣ configure() 내부에서 dequeue를 제거
2️⃣ 이미 dequeue한 cell을 configure()에 전달
private func createDataSource() {
dataSource = UICollectionViewDiffableDataSource<SearchSection, SearchItem>(collectionView: searchResultCollectionView) { [weak self] collectionView, indexPath, item in
guard let self = self else { return nil }
// ✅ `dequeue`를 한 번만 실행 (기존 코드 유지)
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: SearchResultCell.reuseIdentifier,
for: indexPath
) as? SearchResultCell else {
return UICollectionViewCell()
}
cell.setViewModel(self.viewModel) // ✅ `viewModel`을 한 번만 설정
// ✅ `configure()` 호출 시 `cell`을 직접 전달 (dequeue 제거)
switch item {
case .movie(let movie):
self.configure(cell, with: .movie(movie))
case .tv(let tv):
self.configure(cell, with: .tv(tv))
case .people(let person):
self.configure(cell, with: .people(person))
}
return cell // ✅ `dequeue`한 `cell`을 반환
}
}
✅ 3. configure()에서 dequeue 제거
private func configure(_ cell: SearchResultCell, with item: SearchItem) {
cell.configure(with: item)
}
🎯 🚀 최종 정리
✅ dequeueReusableCell을 한 번만 실행하도록 수정
✅ configure() 내부에서 dequeue하지 않고 기존 cell을 사용
✅ 충돌 없이 viewModel을 SearchResultCell에 전달 가능
✅ UICollectionView의 예상 동작을 따르므로 앱이 정상적으로 실행됨