일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 컴공 #Swift #대학생 #iOS #앱개발 #앱디자인
- ios #boxoffice #영화진흥위원회 #swift #앱 #app #대학생
- iOS #Swift #컴공 #대학생
- iOS #Swift #컴공 #공대생
- java #android #xml #sqlite
- 컴공 #Swift #대학생 #iOS #앱개발
- iOS #Swift
- 컴공 #자바스크립트 #스터디 #JS #대학생
- ios #swift #개발자 #apple
- iOS #Swift #대학생 #개발 #코딩
- web #socket #polling #소켓 #폴링 #네트워크 #웹
- node.js #npm #jest #test #웹테스트
- 컴공 #Swift #대학생 #iOS
- iOS #대학생 #Swift
- Today
- Total
평범한 컴공 대학생의 공부일지
영화 순위 Application 만들기 (2) 본문
※이 iOS 카테고리의 글 들은 학교 강의와 과제를 기반으로 작성한 것입니다.※
이번 게시글에서는 이전 게시글에서 다룬 영화 순위 Application 작업을 더 진행해 보겠습니다.
1) View Controller 코드 추가
- prepare 메서드를 활용하여 DetailViewController의 movieName과 연결
- ViewController는 UIViewController라는 부모 클래스를 사용하고 DetailViewController도 동일한 부모 클래스 사용 중
이를 위해 as! as?를 사용하여 다운 캐스팅 후 사용 -> 현재 코드에서는 as! 를 사용하고 있지만 guard let ~ as?를 사용하는 것이
더욱 안전합니다.
import UIKit
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
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieData : MovieData?
var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt="
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
movieURL += makeYesterDayString()
getData()
}
func makeYesterDayString() -> String {
let calendar = Calendar.current
if let yesterday = calendar.date(byAdding: .day, value: -1, to: Date()) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYYMMdd"
return dateFormatter.string(from: yesterday)
}
return "날짜 변환에 실패하였습니다."
}
func getData(){
guard let url = URL(string: movieURL) else {return}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil { print(error!); return}
guard let JSONdata = data else { return }
print(JSONdata)
// let dataString = String(data: JSONdata, encoding: .utf8)
// print(dataString!)
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
//print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].movieNm)
//print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].audiAcc)
self.movieData = decodedData
DispatchQueue.main.async {
self.table.reloadData()
}
} catch{
print(error)
}
}
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
guard let mRank = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].rank else {return UITableViewCell()}
guard let mName = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm else {return UITableViewCell()}
cell.movieName.text = "[\(mRank)위 \(mName)]"
if let aCnt = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aCount = Int(aCnt)!
let result = numF.string(for: aCount)!+"명"
cell.audiAccumulate.text = "어제 : \(result)"
}
if let aAcc = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aAcc1 = Int(aAcc)!
let result = numF.string(for: aAcc1)!+"명"
cell.audiCount.text = "누적 : \(result)"
}
//print(indexPath.description)
return cell
}
//DetailViewController에 선택한 영화의 제목이 출력되도록 설정
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let dest = segue.destination as! DetailViewController
let myIndexPath = table.indexPathForSelectedRow!
let row = myIndexPath.row
dest.movieName=(movieData?.boxOfficeResult.dailyBoxOfficeList[row].movieNm)!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:"+makeYesterDayString()+")🍿"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "by Taram"
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(indexPath.description)
}
}
2) DetailViewController Label 연결
import UIKit
class DetailViewController: UIViewController {
// test코드
@IBOutlet weak var nameLabel: UILabel!
// test 코드 mycell에서 가져와 잘 출력되는지만 확인
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = movieName
}
}
3) SegueWay 연결 구조
4) URL을 Optional로 지정 안할 시 오류 발생
- URL의 주소가 유효하지 않을 경우 URL 생성이 항상 성공하는 것이 아니기 때문입니다.
URL(string:) 생성자는 주어진 문자열이 유효한 URL 형식이 아닐 경우에 nil을 반환할 수 있습니다.
따라서, 안전하게 URL 객체를 생성하려면 옵셔널로 처리하여 실패할 가능성을 고려하기 위해 Optional로 지정해 줍니다.
import UIKit
import WebKit
class DetailViewController: UIViewController {
//WKWebView는 다른 Framework에 존재하여 따로 import를 해주어야 함
@IBOutlet weak var webView: WKWebView!
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = movieName
//WebView 테스트 코드
//유효하지 않은 URL 주소를 처리하기 위해 guard let으로 처리
guard let url = URL(string: "https://m.naver.com") else { return }
let request = URLRequest(url:url)
webView.load(request)
}
}
5) % 인코딩
import UIKit
import WebKit
class DetailViewController: UIViewController {
//WKWebView는 다른 Framework에 존재하여 따로 import를 해주어야 함
@IBOutlet weak var webView: WKWebView!
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = movieName
//기존 한글이나 일본어 등은 Unicode형식으로 처리
//한글은 UTF-8의 방식으로 맥에서는 3바이트로 처리하기 때문에 %인코딩을 사용
//query뒤 %EC%98%81%ED%99%94%EC%88%9C%EC%9C%84 = 영화순위
//https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=%EC%98%81%ED%99%94%EC%88%9C%EC%9C%84
guard let url = URL(string: "https://m.naver.com") else { return }
let request = URLRequest(url:url)
webView.load(request)
}
}
6) 역 persent(%) 인코딩
import UIKit
import WebKit
class DetailViewController: UIViewController {
//WKWebView는 다른 Framework에 존재하여 따로 import를 해주어야 함
@IBOutlet weak var webView: WKWebView!
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = movieName
//기존 한글이나 일본어 등은 Unicode형식으로 처리
//한글은 UTF-8의 방식으로 맥에서는 3바이트로 처리하기 때문에 %인코딩을 사용
//query뒤 %EC%98%81%ED%99%94%EC%88%9C%EC%9C%84 = 영화순위
//Chrome브라우저에서 URL을 가져올 경우
//https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=%EC%98%81%ED%99%94%EC%88%9C%EC%9C%84
//safari 브라우저에서 가져올 경우 한글로 잘 가져옴
//한글이 들어간 url을 %인코딩을 해주어야 url을 인식할 수 있음
let urlKorString = "https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=영화+순위"
//url persent(%)인코딩
let urlString= urlKorString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url:url)
webView.load(request)
}
}
6-1) safari 브라우저에서 한글 URL로 가져올 수 있는 이유
7) 나무위키, 네이버, 유튜브 연결하기
- DetailViewController에 작업
- 각 나무위키, 네이버, 유튜브 연결이 손쉽게 가능
- WebView를 사용했기 때문에 동영상도 시청 가능합니다.
import UIKit
import WebKit
class DetailViewController: UIViewController {
//WKWebView는 다른 Framework에 존재하여 따로 import를 해주어야 함
@IBOutlet weak var webView: WKWebView!
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = movieName
//기존 한글이나 일본어 등은 Unicode형식으로 처리
//한글은 UTF-8의 방식으로 맥에서는 3바이트로 처리하기 때문에 %인코딩을 사용
//query뒤 %EC%98%81%ED%99%94%EC%88%9C%EC%9C%84 = 영화순위
//Chrome브라우저에서 URL을 가져올 경우
//https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=%EC%98%81%ED%99%94%EC%88%9C%EC%9C%84
//safari 브라우저에서 가져올 경우 한글로 잘 가져옴
//한글이 들어간 url을 %인코딩을 해주어야 url을 인식할 수 있음
//query 뒤에는 검색 옵션이 붙기 때문에 movieName을 입력하면 클릭한 영화의 정보로 이동할 수 있음
//let urlKorString = "https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query="+movieName
//아래와 같이 나무위키 URL도 사용 가능
//let urlKorString = "https://namu.wiki/w/"+movieName
//유튜브도 가능
let urlKorString = "https://www.youtube.com/results?search_query="+movieName
//url persent(%)인코딩
let urlString = urlKorString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url:url)
webView.load(request)
}
}
8) Tab bar Controller 추가
9) 영화관 지도 ViewController 추가
- ctrl 드래그 -> 새로운 ViewController에 연결 -> SequeWay 생성
- MapViewController swift파일 생성 후 연결
- 새로운 ViewController의 이름은 theather로 설정
9-1) Scene 구조
9-2) MapViewController의 코드
- 위에서 작성한 DetailViewController의 구조와 같은 코드
import UIKit
import WebKit
class MapViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
//네이버 지도에서 영화관 검색 시 주변 영화관을 보여주는 URL로 설정
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)
}
}
9-3) theatherScene 실행 화면
10) Label의 Radius 조정
11) App Icon 설정
- App Icon은 1024x1024 사이즈로 조정
12) LaunchScreen(로딩 화면) 작업
13) 제작 결과
- App Icon이 추가된 것을 확인
- LaunchScreen(로딩 화면) 확인
- 메인 화면
Tab Bar Controller의 2가지의 탭 -> BoxOffice : 영화 순위 및 누적, 어제 관객수 출력
-> 영화 제목 클릭 시 해당 영화의 유튜브 검색으로 이동
-> Back 버튼 클릭 시 이전 화면으로 변경
-> theather : 주변 영화관이 무엇이 있는지 출력
-> theather 화면
14) 각 코드 공유 및 Scene 구조
- ViewController
import UIKit
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
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieData : MovieData?
var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=f5eef3421c602c6cb7ea224104795888&targetDt="
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
movieURL += makeYesterDayString()
getData()
}
func makeYesterDayString() -> String {
let calendar = Calendar.current
if let yesterday = calendar.date(byAdding: .day, value: -1, to: Date()) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "YYYYMMdd"
return dateFormatter.string(from: yesterday)
}
return "날짜 변환에 실패하였습니다."
}
func getData(){
guard let url = URL(string: movieURL) else {return}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil { print(error!); return}
guard let JSONdata = data else { return }
print(JSONdata)
// let dataString = String(data: JSONdata, encoding: .utf8)
// print(dataString!)
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
//print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].movieNm)
//print(decodedData.boxOfficeResult.dailyBoxOfficeList[0].audiAcc)
self.movieData = decodedData
DispatchQueue.main.async {
self.table.reloadData()
}
} catch{
print(error)
}
}
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
guard let mRank = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].rank else {return UITableViewCell()}
guard let mName = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm else {return UITableViewCell()}
cell.movieName.text = "[\(mRank)위 \(mName)]"
if let aCnt = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aCount = Int(aCnt)!
let result = numF.string(for: aCount)!+"명"
cell.audiAccumulate.text = "어제 : \(result)"
}
if let aAcc = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aAcc1 = Int(aAcc)!
let result = numF.string(for: aAcc1)!+"명"
cell.audiCount.text = "누적 : \(result)"
}
//print(indexPath.description)
return cell
}
//DetailViewController에 선택한 영화의 제목이 출력되도록 설정
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let dest = segue.destination as! DetailViewController
let myIndexPath = table.indexPathForSelectedRow!
let row = myIndexPath.row
dest.movieName=(movieData?.boxOfficeResult.dailyBoxOfficeList[row].movieNm)!
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:"+makeYesterDayString()+")🍿"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "by Taram"
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//print(indexPath.description)
}
}
- MyTableViewCell
import UIKit
class MyTableViewCell: UITableViewCell {
@IBOutlet weak var movieName: UILabel!
@IBOutlet weak var audiAccumulate: UILabel!
@IBOutlet weak var audiCount: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
- DetailViewController
import UIKit
import WebKit
class DetailViewController: UIViewController {
//WKWebView는 다른 Framework에 존재하여 따로 import를 해주어야 함
@IBOutlet weak var webView: WKWebView!
var movieName = ""
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = movieName
//기존 한글이나 일본어 등은 Unicode형식으로 처리
//한글은 UTF-8의 방식으로 맥에서는 3바이트로 처리하기 때문에 %인코딩을 사용
//query뒤 %EC%98%81%ED%99%94%EC%88%9C%EC%9C%84 = 영화순위
//Chrome브라우저에서 URL을 가져올 경우
//https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query=%EC%98%81%ED%99%94%EC%88%9C%EC%9C%84
//safari 브라우저에서 가져올 경우 한글로 잘 가져옴
//한글이 들어간 url을 %인코딩을 해주어야 url을 인식할 수 있음
//query 뒤에는 검색 옵션이 붙기 때문에 movieName을 입력하면 클릭한 영화의 정보로 이동할 수 있음
//let urlKorString = "https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=0&ie=utf8&query="+movieName
//아래와 같이 나무위키 URL도 사용 가능
//let urlKorString = "https://namu.wiki/w/"+movieName
//유튜브도 가능
let urlKorString = "https://www.youtube.com/results?search_query="+movieName
//url persent(%)인코딩
let urlString = urlKorString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
guard let url = URL(string: urlString) else { return }
let request = URLRequest(url:url)
webView.load(request)
}
}
- MapViewController
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)
}
}
- 전체 구조
이번 박스오피스 영화 순위 애플리케이션을 만들어보며 실 생활에서 자주 사용되는 애플리케이션의 구조를 이해하는데 큰 도움이 되었습니다. LaunchScreen, SegueWay, Webkit View, Tab Bar Controller 등을 사용하면서 계속 머릿속에 제가 자주 사용하는 앱의 구조를 생각하게 되었습니다. 이처럼 배워나가면서 실 생활과 이어지는 부분을 떠올리며 코딩을 하니 더욱 재밌게 느껴진 하루였습니다.
감사합니다 :)
'iOS Swift' 카테고리의 다른 글
Enum(열거형), Struct(구조체), Generic(<>) (1) | 2024.06.13 |
---|---|
박스오피스 어플리케이션 정리 (0) | 2024.06.05 |
영화 순위 Application 만들기(1) (0) | 2024.05.16 |
iOS Swift 영화 순위 어플리케이션 (2) | 2024.04.18 |
iOS Swift 간단한 목록 Application 만들기 (1) | 2024.04.08 |