✅ 목표:
- video 아이템을 클릭하면 YouTube 영상이 앱에서 열리도록 만들기
- 이벤트가 MediaCollectionViewCell → MediaTableViewCell → DetailViewController 로 전달되도록 구현
✅ 한 줄 요약:
사용자가 video 썸네일을 클릭하면 → MediaTableViewCell이 이벤트를 받아서 → DetailViewController가 YouTube 링크를 열어준다!
1️⃣ MediaCollectionViewCell (컬렉션뷰 셀)
✅ 하는 일:
- YouTube 썸네일을 보여줌
- 사용자가 썸네일을 탭하면, 해당 videoKey를 상위로 전달
✅ 쉽게 이해하기:
👉 "이 영상 클릭했어!" 라고 알려줌
👉 "이 videoKey를 가지고 있어!"
protocol MediaCollectionViewCellDelegate: AnyObject {
func didTapVideo(with videoKey: String)
}
class MediaCollectionViewCell: UICollectionViewCell {
weak var delegate: MediaCollectionViewCellDelegate? // ✅ 이벤트를 전달할 delegate 선언
private var videoKey: String? // ✅ 클릭한 YouTube 영상 키 저장
private let thumbnailImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 20
return imageView
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureConstraints()
setupTapGesture() // ✅ 탭 제스처 추가
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(with content: MediaInfo) {
let baseUrl = "https://img.youtube.com/vi/"
switch content {
case .video(let video):
if let key = video.key {
let thumbnailUrl = URL(string: "\(baseUrl)\(key)/hqdefault.jpg")
thumbnailImageView.sd_setImage(with: thumbnailUrl, completed: nil)
videoKey = key // ✅ 클릭 시 사용할 YouTube 영상 키 저장
}
case .poster:
break
}
}
private func setupTapGesture() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapThumbnail))
thumbnailImageView.isUserInteractionEnabled = true
thumbnailImageView.addGestureRecognizer(tapGesture)
}
@objc private func didTapThumbnail() {
guard let videoKey = videoKey else { return }
delegate?.didTapVideo(with: videoKey) // ✅ 이벤트를 `MediaTableViewCell`에 전달
}
}
2️⃣ MediaTableViewCell (컬렉션뷰를 가진 테이블뷰 셀)
✅ 하는 일:
- MediaCollectionViewCell에서 전달받은 videoKey를 다시 DetailViewController로 전달
✅ 쉽게 이해하기:
👉 "이 영상 클릭했어!" 라는 말을 DetailViewController에게 전달
protocol MediaTableViewCellDelegate: AnyObject {
func didTapVideo(with videoKey: String)
}
class MediaTableViewCell: UITableViewCell {
weak var delegate: MediaTableViewCellDelegate? // ✅ `DetailViewController`로 이벤트 전달할 delegate 선언
private let mediaCollectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 10
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
collectionView.showsHorizontalScrollIndicator = true
collectionView.backgroundColor = .clear
return collectionView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupCollectionView()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupCollectionView() {
mediaCollectionView.delegate = self
mediaCollectionView.dataSource = self
mediaCollectionView.register(MediaCollectionViewCell.self, forCellWithReuseIdentifier: MediaCollectionViewCell.reuseIdentifier)
}
}
// MARK: - UICollectionViewDelegate
extension MediaTableViewCell: UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MediaCollectionViewCell.reuseIdentifier, for: indexPath) as? MediaCollectionViewCell else { return UICollectionViewCell() }
let mediaItem = mediaItems[indexPath.item]
cell.delegate = self // ✅ `MediaCollectionViewCell`의 delegate 설정
cell.configure(with: mediaItem)
return cell
}
}
// MARK: - MediaCollectionViewCellDelegate
extension MediaTableViewCell: MediaCollectionViewCellDelegate {
func didTapVideo(with videoKey: String) {
delegate?.didTapVideo(with: videoKey) // ✅ `DetailViewController`로 이벤트 전달
}
}
3️⃣ DetailViewController (YouTube 영상 실행)
✅ 하는 일:
- MediaTableViewCell에서 전달받은 videoKey를 사용해 YouTube 영상 열기
✅ 쉽게 이해하기:
👉 "이 영상 클릭했어!" 를 받으면 YouTube 실행!
class DetailViewController: UIViewController {
private let detailTableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.separatorStyle = .none
tableView.backgroundColor = .clear
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
setupTableViewDelegate()
}
private func setupTableViewDelegate() {
detailTableView.delegate = self
detailTableView.dataSource = self
detailTableView.register(MediaTableViewCell.self, forCellReuseIdentifier: MediaTableViewCell.reuseIdentifier)
}
}
// MARK: - UITableViewDelegate
extension DetailViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let sectionType = DetailSection.allCases[indexPath.section]
switch sectionType {
case .video:
guard let cell = tableView.dequeueReusableCell(withIdentifier: MediaTableViewCell.reuseIdentifier, for: indexPath) as? MediaTableViewCell else { return UITableViewCell() }
cell.delegate = self // ✅ Delegate 설정
cell.configure(with: .video(mediaVideos), type: .video)
return cell
default:
return UITableViewCell()
}
}
}
// MARK: - MediaTableViewCellDelegate
extension DetailViewController: MediaTableViewCellDelegate {
func didTapVideo(with videoKey: String) {
// ✅ YouTube 영상 실행
if let url = URL(string: "https://www.youtube.com/watch?v=\(videoKey)") {
UIApplication.shared.open(url)
}
}
}
최종 데이터 흐름 정리
🎬 사용자가 video 클릭 시 흐름
1️⃣ 사용자가 MediaCollectionViewCell에서 video를 탭
- "이 영상 클릭했어!" 라고 MediaTableViewCell에게 알림
2️⃣ MediaTableViewCell이 DetailViewController에게 전달
- "이 영상 클릭했어! YouTube 열어줘!"
3️⃣ DetailViewController가 YouTube 링크 실행
- "YouTube 실행 완료!"
✅ 왜 didTapVideo 이름을 동일하게 했을까?
1️⃣ 이벤트를 자연스럽게 전달하기 위해
📌 기본 개념:
video를 클릭하면, 이벤트가 위쪽으로 전달
이 과정에서 이름을 같이 쓰면 코드가 깔끔해지고, 가독성이 좋음
📌 이벤트 흐름:
MediaCollectionViewCell → MediaTableViewCell → DetailViewController
protocol MediaCollectionViewCellDelegate: AnyObject {
func didTapVideo(with videoKey: String) // 🎯 1️⃣ 동일한 함수명
}
protocol MediaTableViewCellDelegate: AnyObject {
func didTapVideo(with videoKey: String) // 🎯 2️⃣ 동일한 함수명
}
2️⃣ Delegate 패턴에서는 함수명이 같을수록 좋음
✅ Delegate는 특정 이벤트를 전달하는 역할
✅ 역할이 동일한 경우, 같은 이름의 함수를 쓰면 코드의 가독성이 좋음
❌ 만약, 다르게 지으면 헷갈림 😵💫
예를 들어, 이렇게 다르게 하면?
protocol MediaCollectionViewCellDelegate: AnyObject {
func cellDidTapVideo(with videoKey: String)
}
protocol MediaTableViewCellDelegate: AnyObject {
func tableViewCellDidTapVideo(with videoKey: String)
}
👉 이렇게 되면 연결되는 느낌이 약해지고 코드가 복잡해질 수 있어.
👉 반면, 같은 didTapVideo를 사용하면 이벤트가 깔끔하게 전달됨!
3️⃣ didTapVideo는 역할을 잘 설명하는 좋은 이름
✔ didTapVideo → "사용자가 video를 탭했다!" 라는 직관적인 의미
✔ 굳이 추가적인 설명 없이도 코드를 쉽게 이해할 수 있음
💡 즉, Delegate를 거쳐 이벤트가 전달될 때, 같은 함수명을 쓰면
- 📌 연결이 명확해지고
- 📌 코드가 더 직관적이고 이해하기 쉬워짐! 🎯
'Project > MovieClip' 카테고리의 다른 글
✅ 영화 평점 기능 구현 (0) | 2025.02.13 |
---|---|
thumnailImageView에서 .video 와 .poster 분기 처리하기 (0) | 2025.02.12 |
상세 페이지 내에서 영화, 티비 관련한 유사한 내용은 컬렉션뷰로 보여주기 (0) | 2025.02.11 |
상세페이지에서 video, poster 데이터를 UICollectionView로 구현 (0) | 2025.02.11 |
헷갈리기 쉬운 주요 출연진 정보를 받아오는 로직 (0) | 2025.02.10 |