카테고리 없음

iOS 프로그래밍 실무 12주차

sw0913 2025. 5. 21. 16:46

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)
    }
}