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의 예상 동작을 따르므로 앱이 정상적으로 실행됨