1. present 방식이란?
- UIViewController의 present(_:animated:completion:) 메서드를 사용해서
새로운 ViewController를 현재 ViewController 위에 모달로 표시할 수 있습니다. - 이때, 새로 띄운 ViewController를 presentedViewController,
띄운 쪽을 presentingViewController라고 부릅니다. - iOS 13 이후에는 기본적으로 시트(sheet) 형태로 나타나지만,
전체 화면(Full Screen)으로 띄우는 것도 가능합니다.
- root view controller는 UIWindow의 가장 첫 번째, 최상위 뷰 컨트롤러입니다.
- 앱이 실행될 때 처음 사용자에게 보여지는 메인 뷰 컨트롤러입니다.
- UINavigationController, UITabBarController와 같은 컨테이너 뷰 컨트롤러가 주로 루트로 사용됩니다.
Action Segue vs Manual Segue 비교

view controller에서 Initial View Controller를 체크해주면 가장 먼저 나타내줄 화면이면서 Navigation Controller을 추가하니 자동으로 앞으로 화살표가 넘어가는 것을 볼 수 있어요

prepare(for:sender:) 메소드는 iOS 앱에서 스토리보드 기반의 화면 전환(Segue)이 일어나기 직전에 호출되는 메소드입니다.
이 메소드는 데이터 전달, 화면 설정 등 전환될 ViewController에 필요한 준비 작업을 할 때 사용됩니다
- segue: 현재 실행되는 Segue 객체로, identifier, source, destination 등의 정보를 포함합니다.
- sender: Segue를 트리거한 객체(예: 버튼, 셀 등)입니다
- Segue가 발생하기 직전에 호출되어,
전환될 ViewController에 데이터 전달이나 화면 초기 설정을 할 수 있습니다. - 여러 Segue가 한 ViewController에서 발생할 수 있으므로,
segue.identifier를 활용해 분기 처리를 합니다

여기서 다운 캐스팅을 해줘야 합니다

이렇게하면 영화제목을 눌렀을 때 위에 제목을 불러올 수 있어요

네이버 검색에서 영화 제목을 검색 했을 때 url을 불러와서 마지막 query부분까지 가져와서 뒤에 +movieName을 추가해주면 영화 제목을 클릭했을 때 네이버에 영화 제목에 따른 검색 정보가 나오는 걸 볼 수 있었습니다.
mvc 패턴 디자인
Movie.swift (Model)
import Foundation
struct MovieData: Codable {
let boxOfficeResult: BoxOfficeResult
}
struct BoxOfficeResult: Codable {
let dailyBoxOfficeList: [DailyBoxOfficeList]
}
struct DailyBoxOfficeList: Codable {
let movieNm: String
let audiCnt: String
let audiAcc: String
let rank: String
}
MovieService.swift (Model - 네트워크 담당)
import Foundation
class MovieService {
static let shared = MovieService()
private init() {}
private let baseURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key="
func fetchMovies(for date: String, completion: @escaping (MovieData?) -> Void) {
let urlString = baseURL + date
guard let url = URL(string: urlString) else {
completion(nil)
return
}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, _, error in
guard let data = data, error == nil else {
completion(nil)
return
}
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: data)
completion(decodedData)
} catch {
completion(nil)
}
}
task.resume()
}
}
- MyTableViewCell.swift (View)
import UIKit
class MyTableViewCell: UITableViewCell {
@IBOutlet weak var movieName: UILabel!
@IBOutlet weak var audiAccumulate: UILabel!
@IBOutlet weak var audiCount: UILabel!
}
ViewController.swift (Controller)
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieData: MovieData?
override func viewDidLoad() {
super.viewDidLoad()
table.delegate = self
table.dataSource = self
fetchBoxOffice()
}
func fetchBoxOffice() {
let date = makeYesterdayString()
MovieService.shared.fetchMovies(for: date) { [weak self] data in
guard let self = self else { return }
DispatchQueue.main.async {
self.movieData = data
self.table.reloadData()
}
}
}
func makeYesterdayString() -> String {
let y = Calendar.current.date(byAdding: .day, value: -1, to: Date())!
let dateF = DateFormatter()
dateF.dateFormat = "yyyyMMdd"
return dateF.string(from: y)
}
// MARK: - TableView DataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieData?.boxOfficeResult.dailyBoxOfficeList.count ?? 0
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:" + makeYesterdayString() + ")🍿"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "made by Smile LEESW"
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
guard let movie = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row] else { return UITableViewCell() }
cell.movieName.text = "[\(movie.rank)위] \(movie.movieNm)"
let numF = NumberFormatter()
numF.numberStyle = .decimal
if let aAcc = Int(movie.audiAcc) {
cell.audiAccumulate.text = "누적 : \(numF.string(for: aAcc)! )명"
}
if let aCnt = Int(movie.audiCnt) {
cell.audiCount.text = "어제 : \(numF.string(for: aCnt)! )명"
}
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// MARK: - 화면 전환 (Segue)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let dest = segue.destination as! DetailViewController
if let indexPath = table.indexPathForSelectedRow,
let movie = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row] {
dest.movieName = movie.movieNm
}
}
}
}
DetailViewController.swift (Controller)
import UIKit
import WebKit
class DetailViewController: UIViewController {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var webView: WKWebView!
var movieName: String = ""
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = movieName
navigationItem.title = movieName
let urlKorString = "https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=" + movieName
let urlString = urlKorString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
webView.load(request)
}
}
MapViewController.swift (Controller)
import UIKit
import WebKit
class MapViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let urlKorString = "https://map.naver.com/p/search/영화관"
let urlString = urlKorString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url: url)
webView.load(request)
}
}
