✅ Upcoming 섹션 설정
- 먼저 Upcoming 영화 목록을 불러올 테이블을 선언
- 테이블을 사용하기 위한 대리자 선언
- 테이블 셀에 들어갈 테이블뷰 셀을 선언
- 이떄 서버로부터 API를 받아와 데이터를 받아오는 함수를 선언
- UpcomingController 에서 받아온 데이터를 셀로 전달한다. (대리자 부분)
- 그럼 끝
✅ Upcoming 섹션 설정
- 네비게이션 타이틀 설정
import UIKit
class UpcomingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Upcoming"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
}
}
- Upcoming 데이터를 보여주기 위한 목록 설정
import UIKit
class UpcomingViewController: UIViewController {
private let upcomingTable: UITableView = {
let table = UITableView()
table.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Upcoming"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
view.addSubview(upcomingTable)
upcomingTable.delegate = self
upcomingTable.dataSource = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
upcomingTable.frame = view.bounds
}
}
extension UpcomingViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "test"
return cell
}
}
- Upcoming 데이터를 API로 갖고오기 위한 fetchUpcoming 메서드 선언
// API에서 데이터 갖고 오기
private func fetchUpcoming() {
APICaller.shared.getUpcomingMovies { [weak self] result in
switch result {
case.success(let titles):
self?.titles = titles
DispatchQueue.main.async {
self?.upcomingTable.reloadData()
}
case.failure(let error):
print(error.localizedDescription)
}
}
}
- 데이터가 잘 나오는 지 확인하기 위해 extension의 indexPath 파라미터를 가진 tableView 메서드를 수정
extension UpcomingViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = titles[indexPath.row].original_name ?? titles[indexPath.row].original_title ?? "Unknown"
return cell
}
}
- Upcoming 관련 데이터를 테이블에 넣기 위한 TitleTableViewCell 파일을 생성한다
import UIKit
class TitleTableViewCell: UITableViewCell {
static let identifier = "TitleTableViewCell"
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- viewModel 파일 내에 TitleViewModel 구조체에 프로퍼티를 선언
- 해당 프로퍼티는 영화 제목과 포스터 이미지 주소 관련한 것이다.
import Foundation
struct TitleViewModel {
let titleName: String
let posterURL: String
}
- TitleTableViewCell 파일 내에 들어갈 라벨과 이미지 버튼에 대한 변수를 선언
- configure 라는 메서드는 API를 통해 받아온 데이터 중에서 포스터 이미지 주소, 영화 제목에 대한 정보를 받아와 라벨과 이미지에 할당한다.
import UIKit
class TitleTableViewCell: UITableViewCell {
static let identifier = "TitleTableViewCell"
private let playTitleButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
private let titlePosterUIImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(titleLabel)
contentView.addSubview(titlePosterUIImageView)
contentView.addSubview(playTitleButton)
applyConstraints()
}
private func applyConstraints() {
let titlePosterUIImageViewConstraints = [
titlePosterUIImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
titlePosterUIImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
titlePosterUIImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15),
titlePosterUIImageView.widthAnchor.constraint(equalToConstant: 100)
]
let titleLabelConstraints = [
titleLabel.leadingAnchor.constraint(equalTo: titlePosterUIImageView.trailingAnchor, constant: 20),
titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
]
NSLayoutConstraint.activate(titlePosterUIImageViewConstraints)
NSLayoutConstraint.activate(titleLabelConstraints)
}
public func configure(with model: TitleViewModel) {
guard let url = URL(string: model.posterURL) else { return }
titlePosterUIImageView.sd_setImage(with: url, completed: nil)
titleLabel.text = model.titleName
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- UpcomingViewController의 upcomingTable에 위에 생성한 TitleTableViewCell 파일을 등록한다.
import UIKit
class UpcomingViewController: UIViewController {
private var titles: [Title] = [Title]()
private let upcomingTable: UITableView = {
let table = UITableView()
table.register(TitleTableViewCell.self, forCellReuseIdentifier: TitleTableViewCell.identifier)
return table
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
title = "Upcoming"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationItem.largeTitleDisplayMode = .always
view.addSubview(upcomingTable)
upcomingTable.delegate = self
upcomingTable.dataSource = self
fetchUpcoming()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
upcomingTable.frame = view.bounds
}
// API에서 데이터 갖고 오기
private func fetchUpcoming() {
APICaller.shared.getUpcomingMovies { [weak self] result in
switch result {
case.success(let titles):
self?.titles = titles
DispatchQueue.main.async {
self?.upcomingTable.reloadData()
}
case.failure(let error):
print(error.localizedDescription)
}
}
}
}
extension UpcomingViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return titles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: TitleTableViewCell.identifier, for: indexPath) as? TitleTableViewCell else { return UITableViewCell() }
let title = titles[indexPath.row]
cell.configure(with: TitleViewModel(titleName: (title.original_name ?? title.original_title) ?? "Unknown title name", posterURL: title.poster_path ?? ""))
return cell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 140
}
}
- 최종 TitleTableViewCell 파일
import UIKit
class TitleTableViewCell: UITableViewCell {
static let identifier = "TitleTableViewCell"
private let playTitleButton: UIButton = {
let button = UIButton()
// 시스템 이미지 사이즈 조절
let image = UIImage(systemName: "play.circle", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35))
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(image, for: .normal)
button.tintColor = .label
return button
}()
private let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 2
return label
}()
private let titlePosterUIImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(titleLabel)
contentView.addSubview(titlePosterUIImageView)
contentView.addSubview(playTitleButton)
applyConstraints()
}
private func applyConstraints() {
let titlePosterUIImageViewConstraints = [
titlePosterUIImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
titlePosterUIImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15),
titlePosterUIImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15),
titlePosterUIImageView.widthAnchor.constraint(equalToConstant: 100)
]
let titleLabelConstraints = [
titleLabel.leadingAnchor.constraint(equalTo: titlePosterUIImageView.trailingAnchor, constant: 20),
titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
titleLabel.widthAnchor.constraint(equalToConstant: 200)
]
let playTitleButtonConstraints = [
playTitleButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
playTitleButton.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
]
NSLayoutConstraint.activate(titlePosterUIImageViewConstraints)
NSLayoutConstraint.activate(titleLabelConstraints)
NSLayoutConstraint.activate(playTitleButtonConstraints)
}
public func configure(with model: TitleViewModel) {
guard let url = URL(string: "https://image.tmdb.org/t/p/w500/\(model.posterURL)") else { return }
titlePosterUIImageView.sd_setImage(with: url, completed: nil)
titleLabel.text = model.titleName
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- 버튼 크기가 화면에 비해 작다.
- 시스템 이미지를 크게 하기 위해 이미지로 할당한 다음 이를 버튼에 적용시킨다.
- TitleTableViewCell 에서 playTitleButton 부분을 아래와 같이 수정한다.
private let playTitleButton: UIButton = {
let button = UIButton()
// 시스템 이미지 사이즈 조절
let image = UIImage(systemName: "play.circle", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35))
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(image, for: .normal)
button.tintColor = .label
return button
}()
- 지금까지 적용한 UI
https://youtu.be/Wkilq7mzEKk?si=bBnfbInRPgXJ54DW
'Project > NETFLIX' 카테고리의 다른 글
NETFLIX CLONE 10편 (유튜브 API 갖고오기) (0) | 2024.05.14 |
---|---|
NETFLIX CLONE 9편 (Search) (0) | 2024.05.11 |
NETFLIX CLONE 7편 (오픈소스 SDWebImage, 컬렉션뷰셀 적용) (0) | 2024.05.08 |
NETFLIX CLONE 6편 (String 기능 확장, 섹션별 함수 생성) (0) | 2024.05.08 |
NETFLIX CLONE 5편 (TMDB 사이트 방문, 데이터 모델 구축) (0) | 2024.05.07 |