본문 바로가기

Project/MovieClip

상세 페이지 내에서 영화, 티비 관련한 유사한 내용은 컬렉션뷰로 보여주기

 

📌 DetailViewController → SimilarTableViewCell → SimilarCollectionViewCell 데이터 흐름 정리

 

DetailViewController에서 유사한 영화와 TV 정보를 SimilarTableViewCell을 통해 UICollectionView 형태로 보여줌

SimilarCollectionViewCell을 통해 각 개별 콘텐츠(영화 또는 TV)를 셀에 표시

1️⃣ 구성 요소

  • DetailViewController: 데이터를 가져와 UITableView의 SimilarTableViewCell에 전달
  • SimilarTableViewCell: UICollectionView를 사용하여 SimilarCollectionViewCell을 보여줌
  • SimilarCollectionViewCell: 영화 또는 TV 정보를 개별적으로 표시

 

📌 1. DetailViewController에서 데이터 가져오기 (fetchContentDetail)

class DetailViewController: UIViewController {
    
    // MARK: - Variable    
    // 영화, TV와 유사한 정보 저장
    private var contentSimilarInfo: HomeSection?

...

/// init으로 받아온 데이터를 통해 API 요청
    private func fetchContentDetail() {
        Task {
            do {
                // ✅ switch 문에서 데이터를 fetchedDetail 변수에 저장하고, 이후 한 번만 UI 업데이트를 수행
                var fetchedDetail: ContentDetail?
                var fetchedGenres: [String] = []  // 장르 저장 변수
                var castingList: TopBilledCastInfoWelcome?  // 출연진 목록 저장
                var videoInfo: VideoInfoWelcome?
                var posterInfo: PosterInfoWelcome?
                
                var fetchedSimilarInfo: HomeSection?
                
                switch contentType {
                case .movie:
                    let movieDetail = try await NetworkManager.shared.getMovieDetailInfo(movieID: contentID)
                    fetchedDetail = .movie(movieDetail)
                    fetchedGenres = getGenresFromHomeSection(for: contentID)
                    castingList = try await  NetworkManager.shared.getMovieCastInfo(contentID: contentID)
                    
                    videoInfo = try await NetworkManager.shared.getMovieVideoInfo(contentID: contentID)
                    posterInfo = try await NetworkManager.shared.getMoviePosterInfo(contentID: contentID)
                    
                    // ✅ 유사 정보 가져오기
                    let movieSimiliar = try await NetworkManager.shared.getMovieSimilarInfo(contentID: contentID)
                    fetchedSimilarInfo = .trendingMovies(movieSimiliar)
                    
                    
                case .tv:
                    let tvDetail = try await NetworkManager.shared.getTVDetailInfo(tvID: contentID)
                    fetchedDetail = .tv(tvDetail)
                    fetchedGenres = getGenresFromHomeSection(for: contentID)
                    castingList = try await NetworkManager.shared.getTVCastInfo(contentID: contentID)
                    
                    videoInfo = try await NetworkManager.shared.getTvVideoInfo(contentID: contentID)
                    posterInfo = try await NetworkManager.shared.getTvPosterInfo(contentID: contentID)
                    
                    // ✅ 유사 정보 가져오기
                    let tvSimiliar = try await NetworkManager.shared.getTVSimilarInfo(contentID: contentID)
                    fetchedSimilarInfo = .trendingTVs(tvSimiliar)
                    
                case .people:
                    let peopleDetail = try await NetworkManager.shared.getPeopleDetailInfo(peopleID: contentID)
                    fetchedDetail = .people(peopleDetail)
                    fetchedGenres = getGenresFromHomeSection(for: contentID)
                }
                
                DispatchQueue.main.async {
                    self.detailTableHeaderView()   // 헤더뷰 생성
                    
                    // ✅ API 요청 실패 시 UI 업데이트 방지
                    guard let contentDetail = fetchedDetail else { return }
                    self.contentDetail = contentDetail
                    
                    // ✅ 'CastingList'를 'switch' 문을 통해 저장
                    switch contentDetail {
                    case .movie:
                        if let castingList = castingList {
                            self.movieTopBilledCastInfo = .movie(castingList)
                        }
                        
                        self.mediaVideos = videoInfo?.results ?? []    // ✅ 비디오 데이터 저장
                        self.mediaPosters = posterInfo?.posters ?? [] // ✅ 포스터 데이터 저장
                        
                        // ✅ 유사 정보 저장
                        self.contentSimilarInfo = fetchedSimilarInfo
                        
                    case .tv:
                        if let castingList = castingList {
                            self.movieTopBilledCastInfo = .tv(castingList)
                        }
                        
                        self.mediaVideos = videoInfo?.results ?? []    // ✅ 비디오 데이터 저장
                        self.mediaPosters = posterInfo?.posters ?? [] // ✅ 포스터 데이터 저장
                        
                         // ✅ 유사 정보 저장
                        self.contentSimilarInfo = fetchedSimilarInfo
                        
                    case .people:
                        break
                    }
                    
                    
                    switch contentDetail {
                    case .movie(let movieDetail):
                        self.detailHeaderView?.configure(movieDetail, genres: fetchedGenres)
                    case .tv(let tvDetail):
                        self.detailHeaderView?.configure(tvDetail, genres: fetchedGenres)
                    case .people(let peopleDetail):
                        self.detailHeaderView?.configure(peopleDetail)
                        
                    }
                    
                    self.detailTableView.reloadData()   // ✅ 데이터 로드되면 업데이트
                    
                }
            } catch {
                print("❌ 데이터 로드 실패1: \(error)")
            }
        }
    }

 

🔹 설명

  • NetworkManager를 사용해 getMovieSimilarInfo() 또는 getTVSimilarInfo() API를 호출하여 유사한 콘텐츠 데이터를 가져옴
  • 가져온 데이터를 contentSimilarInfo에 저장
  • detailTableView.reloadData()를 호출하여 테이블뷰 갱신

 

 

📌 2. DetailViewController에서 SimilarTableViewCell로 데이터 전달 (tableView(_:cellForRowAt:))

extension DetailViewController: UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return DetailSection.allCases.count
    }
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        // ✅ 현재 섹션 타입 가져오기
        let sectionType = DetailSection.allCases[indexPath.section]
        
        switch sectionType {
            
        ...
            
        case .similar:
            guard let cell = tableView.dequeueReusableCell(withIdentifier: SimilarTableViewCell.reuseIdentifier, for: indexPath) as? SimilarTableViewCell else { return UITableViewCell() }
            
            if let contentSimilarInfo = contentSimilarInfo {
                cell.configure(with: contentSimilarInfo)   // ✅ SimilarTableViewCell에 데이터 전달
            }
            
            return cell
        }
    }
    
    ,,.

}

🔹 설명

  • .similar 섹션이 호출되면 SimilarTableViewCell을 생성
  • configure(with: contentSimilarInfo)를 호출하여 데이터를 넘겨줌

 

📌 3. SimilarTableViewCell에서 UICollectionView에 데이터 연결


class SimilarTableViewCell: UITableViewCell {
    
    // MARK: - Variable
    static let reuseIdentifier: String = "SimilarTableViewCell"
    
    // ✅ 유사한 영화, tv 정보를 저장
    private var contentSimilarInfo: HomeSection?

	...
    func configure(with contents: HomeSection) {
        self.contentSimilarInfo = contents
        similarCollectionView.reloadData() // ✅ UI 업데이트
    }
    ...

🔹 설명

  • DetailViewController에서 전달된 contentSimilarInfo를 저장
  • similarCollectionView.reloadData()를 호출하여 UI를 업데이트

 

📌 4. SimilarTableViewCell에서 개별 데이터 전달 (cellForItemAt)

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SimilarCollectionViewCell.reuseIdentifier, for: indexPath) as? SimilarCollectionViewCell else { return UICollectionViewCell() }
    
    switch contentSimilarInfo {
    case .trendingMovies(let movies):
        let movie = movies[indexPath.item]
        cell.configure(with: .movie(movie))  // ✅ 개별 영화 데이터 전달
    case .trendingTVs(let tvs):
        let tv = tvs[indexPath.item]
        cell.configure(with: .tv(tv))  // ✅ 개별 TV 데이터 전달
    case .trendingPeoples:
        return UICollectionViewCell()
    case .none:
        return UICollectionViewCell()
    }
    
    return cell
}

🔹 설명

  • contentSimilarInfo를 통해 trendingMovies 또는 trendingTVs에 접근하여 개별 데이터를 가져옴
  • SimilarCollectionViewCell.configure(with:)를 호출하여 데이터를 전달

 

 

📌 5. SimilarCollectionViewCell에서 UI 설정 (configure(with:))

func configure(with content: SimilarContent) {
    switch content {
    case .movie(let movieResult):
        if let posterPath = movieResult.backdropPath, !posterPath.isEmpty {
            guard let url = URL(string: "https://image.tmdb.org/t/p/w500/\(posterPath)") else { return }
            similarImageView.sd_setImage(with: url, completed: nil)
        } else {
            similarImageView.image = UIImage(systemName: "photo.badge.exclamationmark")
        }
        
        let title = movieResult.title
        similarTitleLabel.text = title
        
        let score = movieResult.voteAverage
        scoreLabel.configure(with: score != 0 ? Int(score * 10) : 100)
        
    case .tv(let tvResult):
        if let posterPath = tvResult.backdropPath, !posterPath.isEmpty {
            guard let url = URL(string: "https://image.tmdb.org/t/p/w500/\(posterPath)") else { return }
            similarImageView.sd_setImage(with: url, completed: nil)
        } else {
            similarImageView.image = UIImage(systemName: "photo.badge.exclamationmark")
        }
        
        let name = tvResult.name
        similarTitleLabel.text = name
        
        let score = tvResult.voteAverage
        scoreLabel.configure(with: score != 0 ? Int(score * 10) : 100)
    }
}

🔹 설명

  • SimilarContent을 통해 movieResult 또는 tvResult를 받아 UI를 설정
  • 포스터 이미지와 제목, 평점을 설정하여 화면에 표시

⭐️ SimilarContent 열거형 생성하여 영화, tv 구분

 

  • 새로운 Enum을 생성: 기존의 ContentDetail enum을 그대로 사용할 수 없으니, SimilarContent와 같은 새로운 enum을 만들어서 MovieResult와 TVResult를 처리할 수 있도록 한다.
  • configure 메서드 오버로딩: SimilarCollectionViewCell에서 MovieResultTVResult를 처리할 수 있도록 configure(with content: SimilarContent)을 추가한다.

 

// 📌 유사한 콘텐츠를 위한 Enum 추가
enum SimilarContent {
    case movie(MovieResult)   // 영화 결과
    case tv(TVResult)         // TV 결과
}

 

✅ 최종 정리 - SimilarContent 생성 이유

  1. 기존의 ContentDetail는 MovieDetailInfoWelcome, TVDetailInfoWelcome을 다뤘음.
  2. 그러나 유사한 콘텐츠는 MovieResult, TVResult을 다룸 → 새 enum SimilarContent 생성
  3. 컬렉션뷰의 cellForItemAt에서 SimilarContent를 사용하여 configure 호출
  4. SimilarCollectionViewCell에서 configure(with content: SimilarContent)을 구현하여 데이터를 표시