본문 바로가기

Project/MovieClip

✅ UIScrollView 안에 UITableView를 넣는 것이 올바른가? (상세페이지 구현)

일반적으로 UIScrollView 안에 UITableView를 넣는 것은 권장되지 않습니다.
이유는 두 개의 스크롤 기능이 충돌할 수 있기 때문입니다.

문제점

  1. 스크롤 충돌
    • UIScrollView와 UITableView 모두 수직 스크롤이 가능하기 때문에, 터치 이벤트가 어디로 전달될지 애매해질 수 있음.
    • 예를 들어, UIScrollView가 스크롤을 가로채거나, UITableView가 제대로 스크롤되지 않는 문제 발생 가능.
  2. UITableView의 contentSize 문제
    • UITableView는 자체적으로 contentSize를 계산하여 스크롤을 조정함.
    • UIScrollView 안에 들어가면 UITableView가 자신의 높이를 자동으로 조절하지 못할 수 있음.
  3. 스크롤 동작이 이상해질 가능성
    • UIScrollView가 UITableView의 스크롤을 방해하거나, 반대로 UITableView가 UIScrollView의 스크롤을 막을 수도 있음.

 

어떻게 하면 될까? (해결 방법)

1️⃣ 스크롤뷰를 사용하지 않고 UITableView 자체를 활용하는 방법 (추천)

  • 만약 UITableView 안에 여러 가지 섹션이 필요하다면, 헤더, 푸터, 셀을 활용하여 동적인 UI를 구성하는 것이 가장 좋음.
  • UITableView는 이미 스크롤 기능을 가지고 있으므로 추가적인 UIScrollView가 필요 없음.

🔥 이 방법이 가장 안정적이고 일반적으로 권장되는 방법입니다.

 

2️⃣ UIScrollView 안에 UITableView를 넣어야 한다면?

  • UITableView의 높이를 미리 계산하여 고정하고 UIScrollView 안에 배치해야 함.
  • UITableView가 전체 높이를 가지도록 heightAnchor를 설정하여 UIScrollView 내에서 정상적으로 스크롤되도록 처리.

 

🔥 결론

  • 가장 좋은 방법: UITableView 자체를 사용하여 스크롤뷰를 대체하고, headerView, footerView, section을 활용하여 동적 UI를 구성.
  • UIScrollView 안에 UITableView를 넣어야 한다면, UITableView의 높이를 고정하고, isScrollEnabled = false 설정하여 UIScrollView가 정상적으로 스크롤되도록 처리해야 함.

⚠️ UIScrollView 안에 UITableView를 넣는 것은 일반적으로 비효율적이며, 특별한 경우에만 사용해야 함! 🚀

 

 


 

1️⃣ UIScrollView를 사용할 경우 (비효율적인 경우)

🔹 구현 방식

  • UIScrollView를 생성하고, 그 안에 UIView를 넣고,
    그 안에 여러 개의 UILabel, UICollectionView, UIImageView 등을 배치하여 스크롤 가능하도록 구현.
  • contentSize를 동적으로 조절해야 하고, 내부의 콘텐츠가 많아질수록 레이아웃 관리가 어려워짐.

🔸 단점

성능 이슈: UIScrollView는 모든 데이터를 한 번에 로드해야 해서, 데이터가 많아지면 성능이 저하될 수 있음.
재사용 불가능: UIScrollView 내에서 각각의 UIView를 관리해야 하므로 메모리 낭비가 발생할 수 있음.
동적 높이 조절 문제: UILabel 같은 요소들의 높이를 동적으로 조절해야 하는 경우, 제약 조건이 복잡해지고 레이아웃이 깨질 가능성이 있음.

UIScrollView는 상세 페이지가 매우 간단한 경우(예: 텍스트 몇 개, 이미지 하나)에는 사용할 수 있지만,
일반적으로 영화 상세 페이지처럼 데이터가 많다면 UITableView를 사용하는 것이 더 적합함.
🚀

    private func configureConstraints() {
        addSubview(basicScrollView)
        
        basicScrollView.addSubview(basicView)
        basicView.addSubview(backdropImage)
        basicView.addSubview(posterImage)
        basicView.addSubview(titleLabel)
        basicView.addSubview(releasedDateLabel)
        basicView.addSubview(adultSignImage)
        
        basicScrollView.translatesAutoresizingMaskIntoConstraints = false
        basicView.translatesAutoresizingMaskIntoConstraints = false
        backdropImage.translatesAutoresizingMaskIntoConstraints = false
        posterImage.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        releasedDateLabel.translatesAutoresizingMaskIntoConstraints = false
        adultSignImage.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            
            ...
            
        ])
    }

 

2️⃣ UITableView를 사용할 경우 (실무에서 주로 사용하는 방식)

🔹 구현 방식

  • UITableView를 생성하고, 각 항목을 섹션으로 나누어 관리.
  • 예를 들면:
    1. 🎬 헤더 섹션: 영화 포스터, 배경 이미지, 제목, 개봉일, 평점 (UITableViewHeaderView)
    2. 📝 줄거리 섹션: 영화의 개요 (UITableViewCell)
    3. 🎭 출연진 섹션: 출연 배우 리스트 (UICollectionView 포함)
    4. 🎞 관련 영화 섹션: 비슷한 영화 추천 리스트 (UICollectionView 포함)
    5. 🎤 사용자 리뷰 섹션: 사용자들이 남긴 리뷰 (UITableViewCell)

🔸 장점

재사용성: UITableViewCell과 UICollectionViewCell을 활용하여 메모리를 효율적으로 관리 가능.
동적 레이아웃 가능: UITableView의 automaticDimension을 사용하면, 내용에 맞게 높이를 자동 조절 가능.
퍼포먼스 최적화: 스크롤 시 필요한 부분만 로드되므로 메모리 절약.
코드 유지보수 용이: UITableViewDataSource와 UITableViewDelegate를 활용하여 데이터를 분리하여 관리 가능.

🚀 대부분의 실무에서는 UITableView 기반으로 영화 상세 페이지를 만듦.

 

import UIKit

class MovieDetailViewController: UIViewController {

    private let tableView: UITableView = {
        let tableView = UITableView(frame: .zero, style: .grouped)
        tableView.separatorStyle = .none
        return tableView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .black
        setupTableView()
    }
    
    private func setupTableView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(MovieHeaderTableViewCell.self, forCellReuseIdentifier: MovieHeaderTableViewCell.reuseIdentifier)
        tableView.register(MovieOverviewTableViewCell.self, forCellReuseIdentifier: MovieOverviewTableViewCell.reuseIdentifier)
        tableView.register(MovieCastTableViewCell.self, forCellReuseIdentifier: MovieCastTableViewCell.reuseIdentifier)
        tableView.register(MovieSimilarTableViewCell.self, forCellReuseIdentifier: MovieSimilarTableViewCell.reuseIdentifier)
        
        view.addSubview(tableView)
        tableView.frame = view.bounds
    }
}

extension MovieDetailViewController: UITableViewDelegate, UITableViewDataSource {
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 4 // 🎬 헤더, 📝 줄거리, 🎭 출연진, 🎞 관련 영화
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1 // 각 섹션에 하나의 셀만 존재
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.section {
        case 0:
            let cell = tableView.dequeueReusableCell(withIdentifier: MovieHeaderTableViewCell.reuseIdentifier, for: indexPath) as! MovieHeaderTableViewCell
            cell.configure(movie)
            return cell
        case 1:
            let cell = tableView.dequeueReusableCell(withIdentifier: MovieOverviewTableViewCell.reuseIdentifier, for: indexPath) as! MovieOverviewTableViewCell
            cell.configure(movie.overview)
            return cell
        case 2:
            let cell = tableView.dequeueReusableCell(withIdentifier: MovieCastTableViewCell.reuseIdentifier, for: indexPath) as! MovieCastTableViewCell
            cell.configure(movie.cast)
            return cell
        case 3:
            let cell = tableView.dequeueReusableCell(withIdentifier: MovieSimilarTableViewCell.reuseIdentifier, for: indexPath) as! MovieSimilarTableViewCell
            cell.configure(movie.similarMovies)
            return cell
        default:
            return UITableViewCell()
        }
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return UITableView.automaticDimension // 셀의 높이를 내용에 맞게 자동 조절
    }
}