From 1125d8b400d65a4dbd2ab23752a63fafa9410716 Mon Sep 17 00:00:00 2001 From: eunsung Date: Tue, 16 Apr 2024 23:03:09 +0900 Subject: [PATCH 01/13] =?UTF-8?q?view,=20controller=201=EC=B0=A8=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast.xcodeproj/project.pbxproj | 8 + .../Base.lproj/Main.storyboard | 13 +- .../WeatherForecast/WeatherView.swift | 109 ++++++++++++++ .../WeatherViewController.swift | 141 ++++++++++++++++++ 4 files changed, 265 insertions(+), 6 deletions(-) create mode 100644 WeatherForecast/WeatherForecast/WeatherView.swift create mode 100644 WeatherForecast/WeatherForecast/WeatherViewController.swift diff --git a/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj b/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj index 9b2f170..171ed55 100644 --- a/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj +++ b/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 61C1CA882BCD585500932E4A /* WeatherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C1CA872BCD585500932E4A /* WeatherViewController.swift */; }; + 61C1CA8A2BCD586900932E4A /* WeatherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C1CA892BCD586900932E4A /* WeatherView.swift */; }; C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = C741F66F2B58F00500A4DDC0 /* Weather.swift */; }; C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */; }; C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8E2B21C38100DF0D09 /* SceneDelegate.swift */; }; @@ -19,6 +21,8 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 61C1CA872BCD585500932E4A /* WeatherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewController.swift; sourceTree = ""; }; + 61C1CA892BCD586900932E4A /* WeatherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherView.swift; sourceTree = ""; }; C741F66F2B58F00500A4DDC0 /* Weather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = ""; }; C7743D892B21C38100DF0D09 /* WeatherForecast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeatherForecast.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -66,6 +70,8 @@ C7743D8E2B21C38100DF0D09 /* SceneDelegate.swift */, C7743DA02B21C3B400DF0D09 /* WeatherTableViewCell.swift */, C7743D902B21C38100DF0D09 /* ViewController.swift */, + 61C1CA892BCD586900932E4A /* WeatherView.swift */, + 61C1CA872BCD585500932E4A /* WeatherViewController.swift */, C741F66F2B58F00500A4DDC0 /* Weather.swift */, C7743DA22B21CA8500DF0D09 /* WeatherDetailViewController.swift */, C7743D922B21C38100DF0D09 /* Main.storyboard */, @@ -151,8 +157,10 @@ C7743D912B21C38100DF0D09 /* ViewController.swift in Sources */, C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */, C7743DA32B21CA8600DF0D09 /* WeatherDetailViewController.swift in Sources */, + 61C1CA882BCD585500932E4A /* WeatherViewController.swift in Sources */, C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */, C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */, + 61C1CA8A2BCD586900932E4A /* WeatherView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard b/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard index 4798dc7..61db4ad 100644 --- a/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard +++ b/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard @@ -1,24 +1,25 @@ - + - + + - + - - + + - + diff --git a/WeatherForecast/WeatherForecast/WeatherView.swift b/WeatherForecast/WeatherForecast/WeatherView.swift new file mode 100644 index 0000000..f8117c3 --- /dev/null +++ b/WeatherForecast/WeatherForecast/WeatherView.swift @@ -0,0 +1,109 @@ +// +// WeatherView.swift +// WeatherForecast +// +// Created by EUNSUNG on 4/15/24. +// + +import UIKit + +protocol WeatherViewDelegate { + func setNavigationItem(buttonItem: UIBarButtonItem) + func changeTempUnit() + func refresh() + func setTableViewDelegate(view: WeatherView) +} + +class WeatherView: UIView { + private var delegate:WeatherViewDelegate + + private var tableView: UITableView! + private let refreshControl: UIRefreshControl = UIRefreshControl() + private var icons: [UIImage]? + + init(delegate: WeatherViewDelegate) { + self.delegate = delegate + super.init(frame: .zero) + + initialSetUp() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func getTableView() -> UITableView { + return self.tableView + } + + private func initialSetUp() { + delegate.setNavigationItem(buttonItem: UIBarButtonItem(title: "화씨", image: nil, target: self, action: #selector(onBarButton))) + + layTable() + + refreshControl.addTarget(self, + action: #selector(onRefreshControl), + for: .valueChanged) + + tableView.refreshControl = refreshControl + tableView.register(WeatherTableViewCell.self, forCellReuseIdentifier: "WeatherCell") + + delegate.setTableViewDelegate(view: self) +// tableView.dataSource = self +// tableView.delegate = self + } + + @objc private func onBarButton() { + delegate.changeTempUnit() + delegate.refresh() + tableView.reloadData() + } + + @objc private func onRefreshControl() { + delegate.refresh() + refreshControl.endRefreshing() + tableView.reloadData() + } + + private func layTable() { + tableView = .init(frame: .zero, style: .plain) + self.addSubview(tableView) + tableView.translatesAutoresizingMaskIntoConstraints = false + + let safeArea: UILayoutGuide = self.safeAreaLayoutGuide + + NSLayoutConstraint.activate([ + tableView.topAnchor.constraint(equalTo: safeArea.topAnchor), + tableView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), + tableView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor), + tableView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor) + ]) + } +} + +//extension WeatherView: UITableViewDataSource { +// func numberOfSections(in tableView: UITableView) -> Int { +// return 1 +// } +// +// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { +//// weatherJSON?.weatherForecast.count ?? 0 +// return 10 +// } +// +// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { +// let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) +// +// guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell else { +// return cell +// } +// +// cell.dateLabel.text = "eunduk" +// +// return cell +// } +//} +// +//extension WeatherView: UITableViewDelegate { +// +//} diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift new file mode 100644 index 0000000..ca9d967 --- /dev/null +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -0,0 +1,141 @@ +// +// WeatherViewController.swift +// WeatherForecast +// +// Created by EUNSUNG on 4/15/24. +// + +import UIKit + +class WeatherViewController: UIViewController, WeatherViewDelegate { + + + + private var weatherJSON: WeatherJSON? + private var tempUnit: TempUnit = .metric + private let imageChache: NSCache = NSCache() + private let dateFormatter: DateFormatter = { + let formatter: DateFormatter = DateFormatter() + formatter.locale = .init(identifier: "ko_KR") + formatter.dateFormat = "yyyy-MM-dd(EEEEE) a HH:mm" + return formatter + }() + + override func viewDidLoad() { + super.viewDidLoad() + + + } + + override func loadView() { + view = WeatherView(delegate: self) + + } + + func setNavigationItem(buttonItem: UIBarButtonItem) { + navigationItem.rightBarButtonItem = buttonItem + } + func setTableViewDelegate(view: WeatherView) { + view.getTableView().delegate = self + view.getTableView().dataSource = self + } + func changeTempUnit() { + switch tempUnit { + case .imperial: + tempUnit = .metric + navigationItem.rightBarButtonItem?.title = "섭씨" + case .metric: + tempUnit = .imperial + navigationItem.rightBarButtonItem?.title = "화씨" + } +// refresh() + } + func refresh() { + fetchWeatherJSON() + } +} + +extension WeatherViewController { + private func fetchWeatherJSON() { + + let jsonDecoder: JSONDecoder = .init() + jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase + + guard let data = NSDataAsset(name: "weather")?.data else { + return + } + + let info: WeatherJSON + do { + info = try jsonDecoder.decode(WeatherJSON.self, from: data) + } catch { + print(error.localizedDescription) + return + } + + weatherJSON = info + navigationItem.title = weatherJSON?.city.name + } +} + +extension WeatherViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + weatherJSON?.weatherForecast.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) + + guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell, + let weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] else { + return cell + } + + cell.weatherLabel.text = weatherForecastInfo.weather.main + cell.descriptionLabel.text = weatherForecastInfo.weather.description + cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" + + let date: Date = Date(timeIntervalSince1970: weatherForecastInfo.dt) + cell.dateLabel.text = dateFormatter.string(from: date) + + let iconName: String = weatherForecastInfo.weather.icon + let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png" + + if let image = imageChache.object(forKey: urlString as NSString) { + cell.weatherIcon.image = image + return cell + } + + Task { + guard let url: URL = URL(string: urlString), + let (data, _) = try? await URLSession.shared.data(from: url), + let image: UIImage = UIImage(data: data) else { + return + } + + imageChache.setObject(image, forKey: urlString as NSString) + + if indexPath == tableView.indexPath(for: cell) { + cell.weatherIcon.image = image + } + } + + return cell + } +} + +extension WeatherViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + let detailViewController: WeatherDetailViewController = WeatherDetailViewController() + detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] + detailViewController.cityInfo = weatherJSON?.city + detailViewController.tempUnit = tempUnit + navigationController?.show(detailViewController, sender: self) + } +} From 4afdca0c278507f3d1177d69d5bd23f832d6ea3a Mon Sep 17 00:00:00 2001 From: eunsung Date: Wed, 17 Apr 2024 21:11:21 +0900 Subject: [PATCH 02/13] =?UTF-8?q?ViewController=EB=A5=BC=20view=EC=99=80?= =?UTF-8?q?=20controller=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast/WeatherView.swift | 58 ++++----- .../WeatherViewController.swift | 116 ++++++++---------- 2 files changed, 75 insertions(+), 99 deletions(-) diff --git a/WeatherForecast/WeatherForecast/WeatherView.swift b/WeatherForecast/WeatherForecast/WeatherView.swift index f8117c3..1caaf9a 100644 --- a/WeatherForecast/WeatherForecast/WeatherView.swift +++ b/WeatherForecast/WeatherForecast/WeatherView.swift @@ -11,7 +11,9 @@ protocol WeatherViewDelegate { func setNavigationItem(buttonItem: UIBarButtonItem) func changeTempUnit() func refresh() - func setTableViewDelegate(view: WeatherView) + func cellCount() -> Int + func setCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell + func didSelectRow(tableView: UITableView, indexPath: IndexPath) } class WeatherView: UIView { @@ -32,10 +34,6 @@ class WeatherView: UIView { fatalError("init(coder:) has not been implemented") } - func getTableView() -> UITableView { - return self.tableView - } - private func initialSetUp() { delegate.setNavigationItem(buttonItem: UIBarButtonItem(title: "화씨", image: nil, target: self, action: #selector(onBarButton))) @@ -48,9 +46,8 @@ class WeatherView: UIView { tableView.refreshControl = refreshControl tableView.register(WeatherTableViewCell.self, forCellReuseIdentifier: "WeatherCell") - delegate.setTableViewDelegate(view: self) -// tableView.dataSource = self -// tableView.delegate = self + tableView.dataSource = self + tableView.delegate = self } @objc private func onBarButton() { @@ -81,29 +78,22 @@ class WeatherView: UIView { } } -//extension WeatherView: UITableViewDataSource { -// func numberOfSections(in tableView: UITableView) -> Int { -// return 1 -// } -// -// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { -//// weatherJSON?.weatherForecast.count ?? 0 -// return 10 -// } -// -// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { -// let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) -// -// guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell else { -// return cell -// } -// -// cell.dateLabel.text = "eunduk" -// -// return cell -// } -//} -// -//extension WeatherView: UITableViewDelegate { -// -//} +extension WeatherView: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + 1 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return delegate.cellCount() + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + return delegate.setCell(tableView: tableView, indexPath: indexPath) + } +} + +extension WeatherView: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + delegate.didSelectRow(tableView: tableView, indexPath: indexPath) + } +} diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index ca9d967..9c5f230 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -8,9 +8,6 @@ import UIKit class WeatherViewController: UIViewController, WeatherViewDelegate { - - - private var weatherJSON: WeatherJSON? private var tempUnit: TempUnit = .metric private let imageChache: NSCache = NSCache() @@ -35,59 +32,13 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { func setNavigationItem(buttonItem: UIBarButtonItem) { navigationItem.rightBarButtonItem = buttonItem } - func setTableViewDelegate(view: WeatherView) { - view.getTableView().delegate = self - view.getTableView().dataSource = self - } - func changeTempUnit() { - switch tempUnit { - case .imperial: - tempUnit = .metric - navigationItem.rightBarButtonItem?.title = "섭씨" - case .metric: - tempUnit = .imperial - navigationItem.rightBarButtonItem?.title = "화씨" - } -// refresh() - } - func refresh() { - fetchWeatherJSON() - } -} - -extension WeatherViewController { - private func fetchWeatherJSON() { - - let jsonDecoder: JSONDecoder = .init() - jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase - - guard let data = NSDataAsset(name: "weather")?.data else { - return - } - - let info: WeatherJSON - do { - info = try jsonDecoder.decode(WeatherJSON.self, from: data) - } catch { - print(error.localizedDescription) - return - } - - weatherJSON = info - navigationItem.title = weatherJSON?.city.name - } -} - -extension WeatherViewController: UITableViewDataSource { - func numberOfSections(in tableView: UITableView) -> Int { - 1 - } - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - weatherJSON?.weatherForecast.count ?? 0 + func cellCount() -> Int { + refresh() + return weatherJSON?.weatherForecast.count ?? 0 } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + func setCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell { let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell, @@ -98,44 +49,79 @@ extension WeatherViewController: UITableViewDataSource { cell.weatherLabel.text = weatherForecastInfo.weather.main cell.descriptionLabel.text = weatherForecastInfo.weather.description cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" - + let date: Date = Date(timeIntervalSince1970: weatherForecastInfo.dt) cell.dateLabel.text = dateFormatter.string(from: date) - + let iconName: String = weatherForecastInfo.weather.icon let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png" - + if let image = imageChache.object(forKey: urlString as NSString) { cell.weatherIcon.image = image return cell } - + Task { guard let url: URL = URL(string: urlString), let (data, _) = try? await URLSession.shared.data(from: url), let image: UIImage = UIImage(data: data) else { return } - + imageChache.setObject(image, forKey: urlString as NSString) - + if indexPath == tableView.indexPath(for: cell) { cell.weatherIcon.image = image } } - return cell } -} - -extension WeatherViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + + func didSelectRow(tableView: UITableView, indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) - + let detailViewController: WeatherDetailViewController = WeatherDetailViewController() detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] detailViewController.cityInfo = weatherJSON?.city detailViewController.tempUnit = tempUnit navigationController?.show(detailViewController, sender: self) } + + func changeTempUnit() { + switch tempUnit { + case .imperial: + tempUnit = .metric + navigationItem.rightBarButtonItem?.title = "섭씨" + case .metric: + tempUnit = .imperial + navigationItem.rightBarButtonItem?.title = "화씨" + } +// refresh() + } + func refresh() { + fetchWeatherJSON() + } +} + +extension WeatherViewController { + private func fetchWeatherJSON() { + + let jsonDecoder: JSONDecoder = .init() + jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase + + guard let data = NSDataAsset(name: "weather")?.data else { + return + } + + let info: WeatherJSON + do { + info = try jsonDecoder.decode(WeatherJSON.self, from: data) + } catch { + print(error.localizedDescription) + return + } + + weatherJSON = info + navigationItem.title = weatherJSON?.city.name + } } From ec2f80c5614e41603ce2116cb67ef7412b88d63d Mon Sep 17 00:00:00 2001 From: eunsung Date: Fri, 19 Apr 2024 00:19:53 +0900 Subject: [PATCH 03/13] =?UTF-8?q?fetch=ED=95=A8=EC=88=98=201=EC=B0=A8=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base.lproj/Main.storyboard | 2 +- .../WeatherViewController.swift | 38 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard b/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard index 61db4ad..624b913 100644 --- a/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard +++ b/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard @@ -19,7 +19,7 @@ - + diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index 9c5f230..e2bef8b 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -21,12 +21,11 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { override func viewDidLoad() { super.viewDidLoad() - } override func loadView() { view = WeatherView(delegate: self) - + refresh() } func setNavigationItem(buttonItem: UIBarButtonItem) { @@ -34,7 +33,6 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { } func cellCount() -> Int { - refresh() return weatherJSON?.weatherForecast.count ?? 0 } @@ -96,32 +94,40 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { tempUnit = .imperial navigationItem.rightBarButtonItem?.title = "화씨" } -// refresh() + } func refresh() { - fetchWeatherJSON() + guard let json = decodeWeatherJSON(dataName: "weather") else { + return + } + weatherJSON = json + setTitle(titleText: (weatherJSON?.city.name)!) + } + func setTitle(titleText: String) { + navigationItem.title = titleText } } extension WeatherViewController { - private func fetchWeatherJSON() { - + private func fetchWeatherJSON(dataName: String) -> Data? { + guard let data = NSDataAsset(name: dataName)?.data else { + return nil + } + return data + } + + private func decodeWeatherJSON(dataName: String) -> WeatherJSON? { let jsonDecoder: JSONDecoder = .init() jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase - guard let data = NSDataAsset(name: "weather")?.data else { - return + guard let data = fetchWeatherJSON(dataName: dataName) else { + return nil } - - let info: WeatherJSON do { - info = try jsonDecoder.decode(WeatherJSON.self, from: data) + return try jsonDecoder.decode(WeatherJSON.self, from: data) } catch { print(error.localizedDescription) - return + return nil } - - weatherJSON = info - navigationItem.title = weatherJSON?.city.name } } From 7b75c13bc50cdcaf40fa1e203f744362b4ec0d91 Mon Sep 17 00:00:00 2001 From: eunsung Date: Sun, 21 Apr 2024 00:50:00 +0900 Subject: [PATCH 04/13] =?UTF-8?q?setCell=ED=95=A8=EC=88=98=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherViewController.swift | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index e2bef8b..51bf824 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -28,35 +28,20 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { refresh() } - func setNavigationItem(buttonItem: UIBarButtonItem) { - navigationItem.rightBarButtonItem = buttonItem + func setTitle(titleText: String) { + navigationItem.title = titleText } - func cellCount() -> Int { - return weatherJSON?.weatherForecast.count ?? 0 + private func convertTimeToDateString(timeInterval: TimeInterval) -> String { + let date: Date = Date(timeIntervalSince1970: timeInterval) + return dateFormatter.string(from: date) } - func setCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell { - let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) - - guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell, - let weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] else { - return cell - } - - cell.weatherLabel.text = weatherForecastInfo.weather.main - cell.descriptionLabel.text = weatherForecastInfo.weather.description - cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" - - let date: Date = Date(timeIntervalSince1970: weatherForecastInfo.dt) - cell.dateLabel.text = dateFormatter.string(from: date) - - let iconName: String = weatherForecastInfo.weather.icon - let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png" + private func setImageFromURL(icon: String, imageView: UIImageView) { + let urlString: String = "https://openweathermap.org/img/wn/\(icon)@2x.png" if let image = imageChache.object(forKey: urlString as NSString) { - cell.weatherIcon.image = image - return cell + imageView.image = image } Task { @@ -65,24 +50,14 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { let image: UIImage = UIImage(data: data) else { return } - imageChache.setObject(image, forKey: urlString as NSString) - - if indexPath == tableView.indexPath(for: cell) { - cell.weatherIcon.image = image - } + imageView.image = image } - return cell } - func didSelectRow(tableView: UITableView, indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - - let detailViewController: WeatherDetailViewController = WeatherDetailViewController() - detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] - detailViewController.cityInfo = weatherJSON?.city - detailViewController.tempUnit = tempUnit - navigationController?.show(detailViewController, sender: self) + // protocol function + func setNavigationItem(buttonItem: UIBarButtonItem) { + navigationItem.rightBarButtonItem = buttonItem } func changeTempUnit() { @@ -96,6 +71,7 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { } } + func refresh() { guard let json = decodeWeatherJSON(dataName: "weather") else { return @@ -103,8 +79,36 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { weatherJSON = json setTitle(titleText: (weatherJSON?.city.name)!) } - func setTitle(titleText: String) { - navigationItem.title = titleText + + func cellCount() -> Int { + return weatherJSON?.weatherForecast.count ?? 0 + } + + func setCell(tableView: UITableView, indexPath: IndexPath) -> UITableViewCell { + let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) + + guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell, + let weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] else { + return cell + } + + cell.weatherLabel.text = weatherForecastInfo.weather.main + cell.descriptionLabel.text = weatherForecastInfo.weather.description + cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" + cell.dateLabel.text = convertTimeToDateString(timeInterval: weatherForecastInfo.dt) + setImageFromURL(icon: weatherForecastInfo.weather.icon, imageView: cell.weatherIcon) + + return cell + } + + func didSelectRow(tableView: UITableView, indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + let detailViewController: WeatherDetailViewController = WeatherDetailViewController() + detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] + detailViewController.cityInfo = weatherJSON?.city + detailViewController.tempUnit = tempUnit + navigationController?.show(detailViewController, sender: self) } } From 9875be8c822a99764d5e76d319d511a535d812fa Mon Sep 17 00:00:00 2001 From: eunsung Date: Sun, 21 Apr 2024 19:22:09 +0900 Subject: [PATCH 05/13] =?UTF-8?q?WeatherDetailViewController=20->=20view?= =?UTF-8?q?=EC=99=80=20controller=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast.xcodeproj/project.pbxproj | 4 + .../WeatherForecast/WeatherDetailView.swift | 101 +++++++++++++++ .../WeatherDetailViewController.swift | 121 ++++++------------ .../WeatherForecast/WeatherView.swift | 1 + 4 files changed, 143 insertions(+), 84 deletions(-) create mode 100644 WeatherForecast/WeatherForecast/WeatherDetailView.swift diff --git a/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj b/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj index 171ed55..2c4e276 100644 --- a/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj +++ b/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 61C1CA882BCD585500932E4A /* WeatherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C1CA872BCD585500932E4A /* WeatherViewController.swift */; }; 61C1CA8A2BCD586900932E4A /* WeatherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C1CA892BCD586900932E4A /* WeatherView.swift */; }; + 61E8B7922BD41BD600802B65 /* WeatherDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E8B7912BD41BD600802B65 /* WeatherDetailView.swift */; }; C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = C741F66F2B58F00500A4DDC0 /* Weather.swift */; }; C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */; }; C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8E2B21C38100DF0D09 /* SceneDelegate.swift */; }; @@ -23,6 +24,7 @@ /* Begin PBXFileReference section */ 61C1CA872BCD585500932E4A /* WeatherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewController.swift; sourceTree = ""; }; 61C1CA892BCD586900932E4A /* WeatherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherView.swift; sourceTree = ""; }; + 61E8B7912BD41BD600802B65 /* WeatherDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDetailView.swift; sourceTree = ""; }; C741F66F2B58F00500A4DDC0 /* Weather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = ""; }; C7743D892B21C38100DF0D09 /* WeatherForecast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeatherForecast.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -73,6 +75,7 @@ 61C1CA892BCD586900932E4A /* WeatherView.swift */, 61C1CA872BCD585500932E4A /* WeatherViewController.swift */, C741F66F2B58F00500A4DDC0 /* Weather.swift */, + 61E8B7912BD41BD600802B65 /* WeatherDetailView.swift */, C7743DA22B21CA8500DF0D09 /* WeatherDetailViewController.swift */, C7743D922B21C38100DF0D09 /* Main.storyboard */, C7743D952B21C38200DF0D09 /* Assets.xcassets */, @@ -159,6 +162,7 @@ C7743DA32B21CA8600DF0D09 /* WeatherDetailViewController.swift in Sources */, 61C1CA882BCD585500932E4A /* WeatherViewController.swift in Sources */, C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */, + 61E8B7922BD41BD600802B65 /* WeatherDetailView.swift in Sources */, C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */, 61C1CA8A2BCD586900932E4A /* WeatherView.swift in Sources */, ); diff --git a/WeatherForecast/WeatherForecast/WeatherDetailView.swift b/WeatherForecast/WeatherForecast/WeatherDetailView.swift new file mode 100644 index 0000000..3f90938 --- /dev/null +++ b/WeatherForecast/WeatherForecast/WeatherDetailView.swift @@ -0,0 +1,101 @@ +// +// WeatherDetailView.swift +// WeatherForecast +// +// Created by EUNSUNG on 4/21/24. +// + +import Foundation +import UIKit + +protocol WeatherDetailViewDelegate { + func setValue(weatherGroupLabel: UILabel, weatherDescriptionLabel: UILabel, temperatureLabel: UILabel, feelsLikeLabel: UILabel, maximumTemperatureLable: UILabel, minimumTemperatureLable: UILabel, popLabel: UILabel, humidityLabel: UILabel, sunriseTimeLabel: UILabel, sunsetTimeLabel: UILabel, iconImageView: UIImageView) +} + +class WeatherDetailView: UIView { + private let iconImageView: UIImageView = UIImageView() + private let weatherGroupLabel: UILabel = UILabel() + private let weatherDescriptionLabel: UILabel = UILabel() + private let temperatureLabel: UILabel = UILabel() + private let feelsLikeLabel: UILabel = UILabel() + private let maximumTemperatureLable: UILabel = UILabel() + private let minimumTemperatureLable: UILabel = UILabel() + private let popLabel: UILabel = UILabel() + private let humidityLabel: UILabel = UILabel() + private let sunriseTimeLabel: UILabel = UILabel() + private let sunsetTimeLabel: UILabel = UILabel() + private let spacingView: UIView = UIView() + + private var delegate: WeatherDetailViewDelegate + + init(delegate: WeatherDetailViewDelegate) { + self.delegate = delegate + super.init(frame: .zero) + + setUI() + setValue() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setUI() { + self.backgroundColor = .white + spacingView.backgroundColor = .clear + spacingView.setContentHuggingPriority(.defaultLow, for: .vertical) + weatherGroupLabel.font = .preferredFont(forTextStyle: .largeTitle) + weatherDescriptionLabel.font = .preferredFont(forTextStyle: .largeTitle) + + setStackView() + } + + func setValue() { + delegate.setValue(weatherGroupLabel: self.weatherGroupLabel, weatherDescriptionLabel: self.weatherDescriptionLabel, temperatureLabel: self.temperatureLabel, feelsLikeLabel: self.feelsLikeLabel, maximumTemperatureLable: self.maximumTemperatureLable, minimumTemperatureLable: self.minimumTemperatureLable, popLabel: self.popLabel, humidityLabel: self.humidityLabel, sunriseTimeLabel: self.sunriseTimeLabel, sunsetTimeLabel: self.sunsetTimeLabel, iconImageView: self.iconImageView) + } + + func setStackView() { + let mainStackView: UIStackView = .init(arrangedSubviews: [ + iconImageView, + weatherGroupLabel, + weatherDescriptionLabel, + temperatureLabel, + feelsLikeLabel, + maximumTemperatureLable, + minimumTemperatureLable, + popLabel, + humidityLabel, + sunriseTimeLabel, + sunsetTimeLabel, + spacingView + ]) + + mainStackView.arrangedSubviews.forEach { subview in + guard let subview: UILabel = subview as? UILabel else { return } + subview.textColor = .black + subview.backgroundColor = .clear + subview.numberOfLines = 1 + subview.textAlignment = .center + subview.font = .preferredFont(forTextStyle: .body) + } + + mainStackView.axis = .vertical + mainStackView.alignment = .center + mainStackView.spacing = 8 + self.addSubview(mainStackView) + mainStackView.translatesAutoresizingMaskIntoConstraints = false + + let safeArea: UILayoutGuide = self.safeAreaLayoutGuide + NSLayoutConstraint.activate([ + mainStackView.topAnchor.constraint(equalTo: safeArea.topAnchor), + mainStackView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor), + mainStackView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, + constant: 16), + mainStackView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, + constant: -16), + iconImageView.widthAnchor.constraint(equalTo: iconImageView.heightAnchor), + iconImageView.widthAnchor.constraint(equalTo: safeArea.widthAnchor, + multiplier: 0.3) + ]) + } +} diff --git a/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift b/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift index 69d3dfb..b696511 100644 --- a/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift @@ -6,8 +6,8 @@ import UIKit -class WeatherDetailViewController: UIViewController { - +class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate { + var weatherForecastInfo: WeatherForecastInfo? var cityInfo: City? var tempUnit: TempUnit = .metric @@ -21,77 +21,46 @@ class WeatherDetailViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - initialSetUp() + setNavigationTitle() } - private func initialSetUp() { - view.backgroundColor = .white - + override func loadView() { + view = WeatherDetailView(delegate: self) + } + + private func setNavigationTitle() { guard let listInfo = weatherForecastInfo else { return } let date: Date = Date(timeIntervalSince1970: listInfo.dt) navigationItem.title = dateFormatter.string(from: date) + } + + private func cityDateFormatter() -> DateFormatter { + let formatter: DateFormatter = DateFormatter() + formatter.dateFormat = .none + formatter.timeStyle = .short + formatter.locale = .init(identifier: "ko_KR") - let iconImageView: UIImageView = UIImageView() - let weatherGroupLabel: UILabel = UILabel() - let weatherDescriptionLabel: UILabel = UILabel() - let temperatureLabel: UILabel = UILabel() - let feelsLikeLabel: UILabel = UILabel() - let maximumTemperatureLable: UILabel = UILabel() - let minimumTemperatureLable: UILabel = UILabel() - let popLabel: UILabel = UILabel() - let humidityLabel: UILabel = UILabel() - let sunriseTimeLabel: UILabel = UILabel() - let sunsetTimeLabel: UILabel = UILabel() - let spacingView: UIView = UIView() - spacingView.backgroundColor = .clear - spacingView.setContentHuggingPriority(.defaultLow, for: .vertical) - - let mainStackView: UIStackView = .init(arrangedSubviews: [ - iconImageView, - weatherGroupLabel, - weatherDescriptionLabel, - temperatureLabel, - feelsLikeLabel, - maximumTemperatureLable, - minimumTemperatureLable, - popLabel, - humidityLabel, - sunriseTimeLabel, - sunsetTimeLabel, - spacingView - ]) - - mainStackView.arrangedSubviews.forEach { subview in - guard let subview: UILabel = subview as? UILabel else { return } - subview.textColor = .black - subview.backgroundColor = .clear - subview.numberOfLines = 1 - subview.textAlignment = .center - subview.font = .preferredFont(forTextStyle: .body) + return formatter + } + + private func setIconImage(icon:String, iconImageView: UIImageView) { + Task { + let urlString: String = "https://openweathermap.org/img/wn/\(icon)@2x.png" + + guard let url: URL = URL(string: urlString), + let (data, _) = try? await URLSession.shared.data(from: url), + let image: UIImage = UIImage(data: data) else { + return + } + + iconImageView.image = image } - - weatherGroupLabel.font = .preferredFont(forTextStyle: .largeTitle) - weatherDescriptionLabel.font = .preferredFont(forTextStyle: .largeTitle) - - mainStackView.axis = .vertical - mainStackView.alignment = .center - mainStackView.spacing = 8 - view.addSubview(mainStackView) - mainStackView.translatesAutoresizingMaskIntoConstraints = false - - let safeArea: UILayoutGuide = view.safeAreaLayoutGuide - NSLayoutConstraint.activate([ - mainStackView.topAnchor.constraint(equalTo: safeArea.topAnchor), - mainStackView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor), - mainStackView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, - constant: 16), - mainStackView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, - constant: -16), - iconImageView.widthAnchor.constraint(equalTo: iconImageView.heightAnchor), - iconImageView.widthAnchor.constraint(equalTo: safeArea.widthAnchor, - multiplier: 0.3) - ]) + } + + // protocol function + func setValue(weatherGroupLabel: UILabel, weatherDescriptionLabel: UILabel, temperatureLabel: UILabel, feelsLikeLabel: UILabel, maximumTemperatureLable: UILabel, minimumTemperatureLable: UILabel, popLabel: UILabel, humidityLabel: UILabel, sunriseTimeLabel: UILabel, sunsetTimeLabel: UILabel, iconImageView: UIImageView) { + guard let listInfo = weatherForecastInfo else { return } weatherGroupLabel.text = listInfo.weather.main weatherDescriptionLabel.text = listInfo.weather.description @@ -103,25 +72,9 @@ class WeatherDetailViewController: UIViewController { humidityLabel.text = "습도 : \(listInfo.main.humidity)%" if let cityInfo { - let formatter: DateFormatter = DateFormatter() - formatter.dateFormat = .none - formatter.timeStyle = .short - formatter.locale = .init(identifier: "ko_KR") - sunriseTimeLabel.text = "일출 : \(formatter.string(from: Date(timeIntervalSince1970: cityInfo.sunrise)))" - sunsetTimeLabel.text = "일몰 : \(formatter.string(from: Date(timeIntervalSince1970: cityInfo.sunset)))" - } - - Task { - let iconName: String = listInfo.weather.icon - let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png" - - guard let url: URL = URL(string: urlString), - let (data, _) = try? await URLSession.shared.data(from: url), - let image: UIImage = UIImage(data: data) else { - return - } - - iconImageView.image = image + sunriseTimeLabel.text = "일출 : \(cityDateFormatter().string(from: Date(timeIntervalSince1970: cityInfo.sunrise)))" + sunsetTimeLabel.text = "일몰 : \(cityDateFormatter().string(from: Date(timeIntervalSince1970: cityInfo.sunset)))" } + setIconImage(icon: listInfo.weather.icon, iconImageView: iconImageView) } } diff --git a/WeatherForecast/WeatherForecast/WeatherView.swift b/WeatherForecast/WeatherForecast/WeatherView.swift index 1caaf9a..14985e2 100644 --- a/WeatherForecast/WeatherForecast/WeatherView.swift +++ b/WeatherForecast/WeatherForecast/WeatherView.swift @@ -35,6 +35,7 @@ class WeatherView: UIView { } private func initialSetUp() { + self.backgroundColor = .white delegate.setNavigationItem(buttonItem: UIBarButtonItem(title: "화씨", image: nil, target: self, action: #selector(onBarButton))) layTable() From 2f943ec1babd1836bf9a7a0ace26ecbac38e8f77 Mon Sep 17 00:00:00 2001 From: eunsung Date: Sun, 21 Apr 2024 20:21:23 +0900 Subject: [PATCH 06/13] =?UTF-8?q?WeatherDetailViewController,=20DIP=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Base.lproj/Main.storyboard | 2 +- .../WeatherForecast/SceneDelegate.swift | 15 +++++++++- .../WeatherDetailViewController.swift | 18 +++++++++++- .../WeatherViewController.swift | 29 +++++++++++++++---- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard b/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard index 624b913..aa977cf 100644 --- a/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard +++ b/WeatherForecast/WeatherForecast/Base.lproj/Main.storyboard @@ -1,5 +1,5 @@ - + diff --git a/WeatherForecast/WeatherForecast/SceneDelegate.swift b/WeatherForecast/WeatherForecast/SceneDelegate.swift index 264a1ab..448c760 100644 --- a/WeatherForecast/WeatherForecast/SceneDelegate.swift +++ b/WeatherForecast/WeatherForecast/SceneDelegate.swift @@ -15,7 +15,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let _ = (scene as? UIWindowScene) else { return } +// guard let _ = (scene as? UIWindowScene) else { return } + + guard let scene: UIWindowScene = (scene as? UIWindowScene) else { return } + + let viewController: WeatherViewController = WeatherViewController(presentable: WeatherDetailViewController()) + let navigationController: UINavigationController = UINavigationController(rootViewController: viewController) + navigationController.navigationBar.prefersLargeTitles = true + navigationController.navigationBar.tintColor = .black + + let window: UIWindow = UIWindow(windowScene: scene) + window.rootViewController = navigationController + + self.window = window + window.makeKeyAndVisible() } func sceneDidDisconnect(_ scene: UIScene) { diff --git a/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift b/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift index b696511..03651f4 100644 --- a/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift @@ -6,7 +6,7 @@ import UIKit -class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate { +class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate, WeatherDetailPresentable { var weatherForecastInfo: WeatherForecastInfo? var cityInfo: City? @@ -77,4 +77,20 @@ class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate { } setIconImage(icon: listInfo.weather.icon, iconImageView: iconImageView) } + + func setWeatherForecastInfo(weatherForecaseInfo: WeatherForecastInfo?) { + self.weatherForecastInfo = weatherForecaseInfo + } + + func setCityInfo(cityInfo: City?) { + self.cityInfo = cityInfo + } + + func setTempUnit(tempUnit: TempUnit) { + self.tempUnit = tempUnit + } + + func showDetailViewController(on navigationController: UINavigationController?) { + navigationController?.show(self, sender: self) + } } diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index 51bf824..6a5bca5 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -7,6 +7,13 @@ import UIKit +protocol WeatherDetailPresentable { + func setWeatherForecastInfo(weatherForecaseInfo: WeatherForecastInfo?) + func setCityInfo(cityInfo: City?) + func setTempUnit(tempUnit: TempUnit) + func showDetailViewController(on navigationController: UINavigationController?) +} + class WeatherViewController: UIViewController, WeatherViewDelegate { private var weatherJSON: WeatherJSON? private var tempUnit: TempUnit = .metric @@ -18,6 +25,17 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { return formatter }() + private let presentable: WeatherDetailPresentable + + init(presentable: WeatherDetailPresentable) { + self.presentable = presentable + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() @@ -103,12 +121,11 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { func didSelectRow(tableView: UITableView, indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) - - let detailViewController: WeatherDetailViewController = WeatherDetailViewController() - detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] - detailViewController.cityInfo = weatherJSON?.city - detailViewController.tempUnit = tempUnit - navigationController?.show(detailViewController, sender: self) + + presentable.setWeatherForecastInfo(weatherForecaseInfo: weatherJSON?.weatherForecast[indexPath.row]) + presentable.setCityInfo(cityInfo: weatherJSON?.city) + presentable.setTempUnit(tempUnit: tempUnit) + presentable.showDetailViewController(on: navigationController) } } From d8620a50cd7163385486f2f2d9fc516d2db1b55b Mon Sep 17 00:00:00 2001 From: eunsung Date: Thu, 25 Apr 2024 20:55:03 +0900 Subject: [PATCH 07/13] =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WeatherForecast/WeatherForecast/SceneDelegate.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WeatherForecast/WeatherForecast/SceneDelegate.swift b/WeatherForecast/WeatherForecast/SceneDelegate.swift index 448c760..29d9d7f 100644 --- a/WeatherForecast/WeatherForecast/SceneDelegate.swift +++ b/WeatherForecast/WeatherForecast/SceneDelegate.swift @@ -15,7 +15,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). -// guard let _ = (scene as? UIWindowScene) else { return } guard let scene: UIWindowScene = (scene as? UIWindowScene) else { return } From 4004ec474622cdc358112b3853fba0cdbf96b884 Mon Sep 17 00:00:00 2001 From: eunsung Date: Thu, 25 Apr 2024 21:05:56 +0900 Subject: [PATCH 08/13] =?UTF-8?q?import=20foundation,=20uikit=EC=99=80=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=EB=90=98=EB=AF=80=EB=A1=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WeatherForecast/WeatherForecast/WeatherDetailView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WeatherForecast/WeatherForecast/WeatherDetailView.swift b/WeatherForecast/WeatherForecast/WeatherDetailView.swift index 3f90938..e35c193 100644 --- a/WeatherForecast/WeatherForecast/WeatherDetailView.swift +++ b/WeatherForecast/WeatherForecast/WeatherDetailView.swift @@ -5,7 +5,6 @@ // Created by EUNSUNG on 4/21/24. // -import Foundation import UIKit protocol WeatherDetailViewDelegate { From ff7d44fcc856ef209f90f8af32f3f9bbf75355e0 Mon Sep 17 00:00:00 2001 From: eunsung Date: Thu, 25 Apr 2024 21:29:17 +0900 Subject: [PATCH 09/13] =?UTF-8?q?delegate=20->=20weak=EB=A1=9C=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast/WeatherDetailView.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/WeatherForecast/WeatherForecast/WeatherDetailView.swift b/WeatherForecast/WeatherForecast/WeatherDetailView.swift index e35c193..eefcdf1 100644 --- a/WeatherForecast/WeatherForecast/WeatherDetailView.swift +++ b/WeatherForecast/WeatherForecast/WeatherDetailView.swift @@ -7,7 +7,7 @@ import UIKit -protocol WeatherDetailViewDelegate { +protocol WeatherDetailViewDelegate: class { func setValue(weatherGroupLabel: UILabel, weatherDescriptionLabel: UILabel, temperatureLabel: UILabel, feelsLikeLabel: UILabel, maximumTemperatureLable: UILabel, minimumTemperatureLable: UILabel, popLabel: UILabel, humidityLabel: UILabel, sunriseTimeLabel: UILabel, sunsetTimeLabel: UILabel, iconImageView: UIImageView) } @@ -25,7 +25,12 @@ class WeatherDetailView: UIView { private let sunsetTimeLabel: UILabel = UILabel() private let spacingView: UIView = UIView() - private var delegate: WeatherDetailViewDelegate + private weak var delegate: WeatherDetailViewDelegate? + + // 다음에 swift 5.10으로 업데이트 후 nested protocol 적용해보겠습니다. +// protocol WeatherDetailViewDelegate2 { +// +// } init(delegate: WeatherDetailViewDelegate) { self.delegate = delegate @@ -50,7 +55,7 @@ class WeatherDetailView: UIView { } func setValue() { - delegate.setValue(weatherGroupLabel: self.weatherGroupLabel, weatherDescriptionLabel: self.weatherDescriptionLabel, temperatureLabel: self.temperatureLabel, feelsLikeLabel: self.feelsLikeLabel, maximumTemperatureLable: self.maximumTemperatureLable, minimumTemperatureLable: self.minimumTemperatureLable, popLabel: self.popLabel, humidityLabel: self.humidityLabel, sunriseTimeLabel: self.sunriseTimeLabel, sunsetTimeLabel: self.sunsetTimeLabel, iconImageView: self.iconImageView) + delegate?.setValue(weatherGroupLabel: self.weatherGroupLabel, weatherDescriptionLabel: self.weatherDescriptionLabel, temperatureLabel: self.temperatureLabel, feelsLikeLabel: self.feelsLikeLabel, maximumTemperatureLable: self.maximumTemperatureLable, minimumTemperatureLable: self.minimumTemperatureLable, popLabel: self.popLabel, humidityLabel: self.humidityLabel, sunriseTimeLabel: self.sunriseTimeLabel, sunsetTimeLabel: self.sunsetTimeLabel, iconImageView: self.iconImageView) } func setStackView() { From 1199e75d55ec5f2feb3f509babd4167d398b2e2b Mon Sep 17 00:00:00 2001 From: eunsung Date: Fri, 26 Apr 2024 08:40:11 +0900 Subject: [PATCH 10/13] =?UTF-8?q?imageManager=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=9B=84=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast.xcodeproj/project.pbxproj | 4 +++ .../WeatherForecast/ImageManager.swift | 30 +++++++++++++++++++ .../WeatherForecast/SceneDelegate.swift | 2 +- .../WeatherViewController.swift | 16 +++++----- 4 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 WeatherForecast/WeatherForecast/ImageManager.swift diff --git a/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj b/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj index 2c4e276..d337aef 100644 --- a/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj +++ b/WeatherForecast/WeatherForecast.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 61C1CA882BCD585500932E4A /* WeatherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C1CA872BCD585500932E4A /* WeatherViewController.swift */; }; 61C1CA8A2BCD586900932E4A /* WeatherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61C1CA892BCD586900932E4A /* WeatherView.swift */; }; 61E8B7922BD41BD600802B65 /* WeatherDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E8B7912BD41BD600802B65 /* WeatherDetailView.swift */; }; + 61E8B7942BDA864B00802B65 /* ImageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 61E8B7932BDA864B00802B65 /* ImageManager.swift */; }; C741F6702B58F00500A4DDC0 /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = C741F66F2B58F00500A4DDC0 /* Weather.swift */; }; C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */; }; C7743D8F2B21C38100DF0D09 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7743D8E2B21C38100DF0D09 /* SceneDelegate.swift */; }; @@ -25,6 +26,7 @@ 61C1CA872BCD585500932E4A /* WeatherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewController.swift; sourceTree = ""; }; 61C1CA892BCD586900932E4A /* WeatherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherView.swift; sourceTree = ""; }; 61E8B7912BD41BD600802B65 /* WeatherDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDetailView.swift; sourceTree = ""; }; + 61E8B7932BDA864B00802B65 /* ImageManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageManager.swift; sourceTree = ""; }; C741F66F2B58F00500A4DDC0 /* Weather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = ""; }; C7743D892B21C38100DF0D09 /* WeatherForecast.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeatherForecast.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7743D8C2B21C38100DF0D09 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -77,6 +79,7 @@ C741F66F2B58F00500A4DDC0 /* Weather.swift */, 61E8B7912BD41BD600802B65 /* WeatherDetailView.swift */, C7743DA22B21CA8500DF0D09 /* WeatherDetailViewController.swift */, + 61E8B7932BDA864B00802B65 /* ImageManager.swift */, C7743D922B21C38100DF0D09 /* Main.storyboard */, C7743D952B21C38200DF0D09 /* Assets.xcassets */, C7743D972B21C38200DF0D09 /* LaunchScreen.storyboard */, @@ -157,6 +160,7 @@ buildActionMask = 2147483647; files = ( C7743DA12B21C3B400DF0D09 /* WeatherTableViewCell.swift in Sources */, + 61E8B7942BDA864B00802B65 /* ImageManager.swift in Sources */, C7743D912B21C38100DF0D09 /* ViewController.swift in Sources */, C7743D8D2B21C38100DF0D09 /* AppDelegate.swift in Sources */, C7743DA32B21CA8600DF0D09 /* WeatherDetailViewController.swift in Sources */, diff --git a/WeatherForecast/WeatherForecast/ImageManager.swift b/WeatherForecast/WeatherForecast/ImageManager.swift new file mode 100644 index 0000000..470923d --- /dev/null +++ b/WeatherForecast/WeatherForecast/ImageManager.swift @@ -0,0 +1,30 @@ +// +// ImageManager.swift +// WeatherForecast +// +// Created by EUNSUNG on 4/25/24. +// + +import UIKit + +struct ImageManager { + private let imageChache: NSCache = NSCache() + + func downloadImage(urlString: String) async -> UIImage? { + guard let url: URL = URL(string: urlString), + let (data, _) = try? await URLSession.shared.data(from: url), + let image: UIImage = UIImage(data: data) else { + return nil + } + return image + } + + func getImageChache(forKey: String) -> UIImage? { + guard let image = imageChache.object(forKey: forKey as NSString) else { return nil } + return image + } + + func setImageChache(image: UIImage, forKey: String) { + imageChache.setObject(image, forKey: forKey as NSString) + } +} diff --git a/WeatherForecast/WeatherForecast/SceneDelegate.swift b/WeatherForecast/WeatherForecast/SceneDelegate.swift index 29d9d7f..23a09c7 100644 --- a/WeatherForecast/WeatherForecast/SceneDelegate.swift +++ b/WeatherForecast/WeatherForecast/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let scene: UIWindowScene = (scene as? UIWindowScene) else { return } - let viewController: WeatherViewController = WeatherViewController(presentable: WeatherDetailViewController()) + let viewController: WeatherViewController = WeatherViewController(presentable: WeatherDetailViewController(), imageManager: ImageManager()) let navigationController: UINavigationController = UINavigationController(rootViewController: viewController) navigationController.navigationBar.prefersLargeTitles = true navigationController.navigationBar.tintColor = .black diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index 6a5bca5..74c1eec 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -17,7 +17,6 @@ protocol WeatherDetailPresentable { class WeatherViewController: UIViewController, WeatherViewDelegate { private var weatherJSON: WeatherJSON? private var tempUnit: TempUnit = .metric - private let imageChache: NSCache = NSCache() private let dateFormatter: DateFormatter = { let formatter: DateFormatter = DateFormatter() formatter.locale = .init(identifier: "ko_KR") @@ -26,9 +25,11 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { }() private let presentable: WeatherDetailPresentable + private let imageManager: ImageManager - init(presentable: WeatherDetailPresentable) { + init(presentable: WeatherDetailPresentable, imageManager: ImageManager) { self.presentable = presentable + self.imageManager = imageManager super.init(nibName: nil, bundle: nil) } @@ -57,18 +58,15 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { private func setImageFromURL(icon: String, imageView: UIImageView) { let urlString: String = "https://openweathermap.org/img/wn/\(icon)@2x.png" - - if let image = imageChache.object(forKey: urlString as NSString) { + + if let image = imageManager.getImageChache(forKey: urlString) { imageView.image = image } - Task { - guard let url: URL = URL(string: urlString), - let (data, _) = try? await URLSession.shared.data(from: url), - let image: UIImage = UIImage(data: data) else { + guard let image = await imageManager.downloadImage(urlString: urlString) else { return } - imageChache.setObject(image, forKey: urlString as NSString) + imageManager.setImageChache(image: image, forKey: urlString) imageView.image = image } } From 5cc3f1cd8fdafccdaf3752d5e9bba3cd7680e98c Mon Sep 17 00:00:00 2001 From: eunsung Date: Fri, 26 Apr 2024 08:41:54 +0900 Subject: [PATCH 11/13] =?UTF-8?q?loadView=EC=97=90=EC=84=9C=EB=8A=94=20vie?= =?UTF-8?q?w=EB=A5=BC=20load=ED=95=98=EB=8A=94=20=EB=8F=99=EC=9E=91?= =?UTF-8?q?=EB=A7=8C=20=EC=88=98=ED=96=89=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WeatherForecast/WeatherForecast/WeatherViewController.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index 74c1eec..0f1036a 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -39,12 +39,11 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { override func viewDidLoad() { super.viewDidLoad() - + refresh() } override func loadView() { view = WeatherView(delegate: self) - refresh() } func setTitle(titleText: String) { From 3a9868b7ace57d4752f0dc85f93bacd8888a917b Mon Sep 17 00:00:00 2001 From: eunsung Date: Fri, 26 Apr 2024 21:39:37 +0900 Subject: [PATCH 12/13] =?UTF-8?q?changeTempUnit=20=ED=86=A0=EA=B8=80?= =?UTF-8?q?=ED=98=95=ED=83=9C=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast/WeatherViewController.swift | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index 0f1036a..f9145d2 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -76,15 +76,8 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { } func changeTempUnit() { - switch tempUnit { - case .imperial: - tempUnit = .metric - navigationItem.rightBarButtonItem?.title = "섭씨" - case .metric: - tempUnit = .imperial - navigationItem.rightBarButtonItem?.title = "화씨" - } - + tempUnit = tempUnit == .imperial ? .metric : .imperial + navigationItem.rightBarButtonItem?.title = tempUnit == .imperial ? "화씨" : "섭씨" } func refresh() { From fd07a9b07d711dd90294814837d93d85bb964b62 Mon Sep 17 00:00:00 2001 From: eunsung Date: Fri, 26 Apr 2024 23:58:04 +0900 Subject: [PATCH 13/13] =?UTF-8?q?changeTempUnit=20=ED=86=A0=EA=B8=80?= =?UTF-8?q?=EB=A1=9C=20=EB=8F=99=EC=9E=91=ED=95=98=EA=B2=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20OCP=20=EC=A0=81=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=ED=99=94=EC=94=A8=20=EC=84=AD=EC=94=A8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../WeatherForecast/ViewController.swift | 330 +++++++++--------- WeatherForecast/WeatherForecast/Weather.swift | 41 ++- .../WeatherDetailViewController.swift | 15 +- .../WeatherViewController.swift | 18 +- 4 files changed, 216 insertions(+), 188 deletions(-) diff --git a/WeatherForecast/WeatherForecast/ViewController.swift b/WeatherForecast/WeatherForecast/ViewController.swift index 50b66fb..4ce79cf 100644 --- a/WeatherForecast/WeatherForecast/ViewController.swift +++ b/WeatherForecast/WeatherForecast/ViewController.swift @@ -1,166 +1,166 @@ +//// +//// WeatherForecast - ViewController.swift +//// Created by yagom. +//// Copyright © yagom. All rights reserved. +//// +// +//import UIKit +// +//class ViewController: UIViewController { +// var tableView: UITableView! +// let refreshControl: UIRefreshControl = UIRefreshControl() +// var weatherJSON: WeatherJSON? +// var icons: [UIImage]? +// let imageChache: NSCache = NSCache() +// let dateFormatter: DateFormatter = { +// let formatter: DateFormatter = DateFormatter() +// formatter.locale = .init(identifier: "ko_KR") +// formatter.dateFormat = "yyyy-MM-dd(EEEEE) a HH:mm" +// return formatter +// }() +// +// var tempUnit: TempUnit = .metric +// +// override func viewDidLoad() { +// super.viewDidLoad() +// initialSetUp() +// } +//} +// +//extension ViewController { +// @objc private func changeTempUnit() { +// switch tempUnit { +// case .imperial: +// tempUnit = .metric +// navigationItem.rightBarButtonItem?.title = "섭씨" +// case .metric: +// tempUnit = .imperial +// navigationItem.rightBarButtonItem?.title = "화씨" +// } +// refresh() +// } +// +// @objc private func refresh() { +// fetchWeatherJSON() +// tableView.reloadData() +// refreshControl.endRefreshing() +// } +// +// private func initialSetUp() { +// navigationItem.rightBarButtonItem = UIBarButtonItem(title: "화씨", image: nil, target: self, action: #selector(changeTempUnit)) +// +// layTable() +// +// refreshControl.addTarget(self, +// action: #selector(refresh), +// for: .valueChanged) +// +// tableView.refreshControl = refreshControl +// tableView.register(WeatherTableViewCell.self, forCellReuseIdentifier: "WeatherCell") +// tableView.dataSource = self +// tableView.delegate = self +// } +// +// private func layTable() { +// tableView = .init(frame: .zero, style: .plain) +// view.addSubview(tableView) +// tableView.translatesAutoresizingMaskIntoConstraints = false +// +// let safeArea: UILayoutGuide = view.safeAreaLayoutGuide +// +// NSLayoutConstraint.activate([ +// tableView.topAnchor.constraint(equalTo: safeArea.topAnchor), +// tableView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), +// tableView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor), +// tableView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor) +// ]) +// } +//} +// +//extension ViewController { +// private func fetchWeatherJSON() { +// +// let jsonDecoder: JSONDecoder = .init() +// jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase +// +// guard let data = NSDataAsset(name: "weather")?.data else { +// return +// } +// +// let info: WeatherJSON +// do { +// info = try jsonDecoder.decode(WeatherJSON.self, from: data) +// } catch { +// print(error.localizedDescription) +// return +// } +// +// weatherJSON = info +// navigationItem.title = weatherJSON?.city.name +// } +//} +// +//extension ViewController: UITableViewDataSource { +// +// func numberOfSections(in tableView: UITableView) -> Int { +// 1 +// } +// +// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { +// weatherJSON?.weatherForecast.count ?? 0 +// } +// +// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { +// let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) +// +// guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell, +// let weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] else { +// return cell +// } +// +// cell.weatherLabel.text = weatherForecastInfo.weather.main +// cell.descriptionLabel.text = weatherForecastInfo.weather.description +// cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" +// +// let date: Date = Date(timeIntervalSince1970: weatherForecastInfo.dt) +// cell.dateLabel.text = dateFormatter.string(from: date) +// +// let iconName: String = weatherForecastInfo.weather.icon +// let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png" +// +// if let image = imageChache.object(forKey: urlString as NSString) { +// cell.weatherIcon.image = image +// return cell +// } +// +// Task { +// guard let url: URL = URL(string: urlString), +// let (data, _) = try? await URLSession.shared.data(from: url), +// let image: UIImage = UIImage(data: data) else { +// return +// } +// +// imageChache.setObject(image, forKey: urlString as NSString) +// +// if indexPath == tableView.indexPath(for: cell) { +// cell.weatherIcon.image = image +// } +// } +// +// return cell +// } +//} +// +//extension ViewController: UITableViewDelegate { +// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { +// tableView.deselectRow(at: indexPath, animated: true) +// +// let detailViewController: WeatherDetailViewController = WeatherDetailViewController() +// detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] +// detailViewController.cityInfo = weatherJSON?.city +// detailViewController.tempUnit = tempUnit +// navigationController?.show(detailViewController, sender: self) +// } +//} +// // -// WeatherForecast - ViewController.swift -// Created by yagom. -// Copyright © yagom. All rights reserved. -// - -import UIKit - -class ViewController: UIViewController { - var tableView: UITableView! - let refreshControl: UIRefreshControl = UIRefreshControl() - var weatherJSON: WeatherJSON? - var icons: [UIImage]? - let imageChache: NSCache = NSCache() - let dateFormatter: DateFormatter = { - let formatter: DateFormatter = DateFormatter() - formatter.locale = .init(identifier: "ko_KR") - formatter.dateFormat = "yyyy-MM-dd(EEEEE) a HH:mm" - return formatter - }() - - var tempUnit: TempUnit = .metric - - override func viewDidLoad() { - super.viewDidLoad() - initialSetUp() - } -} - -extension ViewController { - @objc private func changeTempUnit() { - switch tempUnit { - case .imperial: - tempUnit = .metric - navigationItem.rightBarButtonItem?.title = "섭씨" - case .metric: - tempUnit = .imperial - navigationItem.rightBarButtonItem?.title = "화씨" - } - refresh() - } - - @objc private func refresh() { - fetchWeatherJSON() - tableView.reloadData() - refreshControl.endRefreshing() - } - - private func initialSetUp() { - navigationItem.rightBarButtonItem = UIBarButtonItem(title: "화씨", image: nil, target: self, action: #selector(changeTempUnit)) - - layTable() - - refreshControl.addTarget(self, - action: #selector(refresh), - for: .valueChanged) - - tableView.refreshControl = refreshControl - tableView.register(WeatherTableViewCell.self, forCellReuseIdentifier: "WeatherCell") - tableView.dataSource = self - tableView.delegate = self - } - - private func layTable() { - tableView = .init(frame: .zero, style: .plain) - view.addSubview(tableView) - tableView.translatesAutoresizingMaskIntoConstraints = false - - let safeArea: UILayoutGuide = view.safeAreaLayoutGuide - - NSLayoutConstraint.activate([ - tableView.topAnchor.constraint(equalTo: safeArea.topAnchor), - tableView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), - tableView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor), - tableView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor) - ]) - } -} - -extension ViewController { - private func fetchWeatherJSON() { - - let jsonDecoder: JSONDecoder = .init() - jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase - - guard let data = NSDataAsset(name: "weather")?.data else { - return - } - - let info: WeatherJSON - do { - info = try jsonDecoder.decode(WeatherJSON.self, from: data) - } catch { - print(error.localizedDescription) - return - } - - weatherJSON = info - navigationItem.title = weatherJSON?.city.name - } -} - -extension ViewController: UITableViewDataSource { - - func numberOfSections(in tableView: UITableView) -> Int { - 1 - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - weatherJSON?.weatherForecast.count ?? 0 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "WeatherCell", for: indexPath) - - guard let cell: WeatherTableViewCell = cell as? WeatherTableViewCell, - let weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] else { - return cell - } - - cell.weatherLabel.text = weatherForecastInfo.weather.main - cell.descriptionLabel.text = weatherForecastInfo.weather.description - cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" - - let date: Date = Date(timeIntervalSince1970: weatherForecastInfo.dt) - cell.dateLabel.text = dateFormatter.string(from: date) - - let iconName: String = weatherForecastInfo.weather.icon - let urlString: String = "https://openweathermap.org/img/wn/\(iconName)@2x.png" - - if let image = imageChache.object(forKey: urlString as NSString) { - cell.weatherIcon.image = image - return cell - } - - Task { - guard let url: URL = URL(string: urlString), - let (data, _) = try? await URLSession.shared.data(from: url), - let image: UIImage = UIImage(data: data) else { - return - } - - imageChache.setObject(image, forKey: urlString as NSString) - - if indexPath == tableView.indexPath(for: cell) { - cell.weatherIcon.image = image - } - } - - return cell - } -} - -extension ViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - tableView.deselectRow(at: indexPath, animated: true) - - let detailViewController: WeatherDetailViewController = WeatherDetailViewController() - detailViewController.weatherForecastInfo = weatherJSON?.weatherForecast[indexPath.row] - detailViewController.cityInfo = weatherJSON?.city - detailViewController.tempUnit = tempUnit - navigationController?.show(detailViewController, sender: self) - } -} - - diff --git a/WeatherForecast/WeatherForecast/Weather.swift b/WeatherForecast/WeatherForecast/Weather.swift index ede7585..39a96b2 100644 --- a/WeatherForecast/WeatherForecast/Weather.swift +++ b/WeatherForecast/WeatherForecast/Weather.swift @@ -22,10 +22,33 @@ class WeatherForecastInfo: Decodable { // MARK: - MainClass class MainInfo: Decodable { + //let temp: Temperature let temp, feelsLike, tempMin, tempMax: Double let pressure, seaLevel, grndLevel, humidity, pop: Double } +protocol Temperature { + var unit: String { get } + var unitString: String { get } + func getTemperature(temp: Double) -> Double +} + +struct MetricTemperature: Temperature { + var unit: String = "℃" + var unitString: String = "화씨" + func getTemperature(temp: Double) -> Double { + return temp + } +} + +struct ImperialTemperature: Temperature { + var unit: String = "℉" + var unitString: String = "섭씨" + func getTemperature(temp: Double) -> Double { + return temp / 2 // 화씨 임시 로직 + } +} + // MARK: - Weather class Weather: Decodable { let id: Int @@ -50,13 +73,13 @@ class Coord: Decodable { } // MARK: - Temperature Unit -enum TempUnit: String { - case metric, imperial - var expression: String { - switch self { - case .metric: return "℃" - case .imperial: return "℉" - } - } -} +//enum TempUnit: String { +// case metric, imperial +// var expression: String { +// switch self { +// case .metric: return "℃" +// case .imperial: return "℉" +// } +// } +//} diff --git a/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift b/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift index 03651f4..f6bdee7 100644 --- a/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherDetailViewController.swift @@ -10,7 +10,7 @@ class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate, var weatherForecastInfo: WeatherForecastInfo? var cityInfo: City? - var tempUnit: TempUnit = .metric + var temperature: Temperature? let dateFormatter: DateFormatter = { let formatter: DateFormatter = DateFormatter() @@ -64,10 +64,11 @@ class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate, weatherGroupLabel.text = listInfo.weather.main weatherDescriptionLabel.text = listInfo.weather.description - temperatureLabel.text = "현재 기온 : \(listInfo.main.temp)\(tempUnit.expression)" - feelsLikeLabel.text = "체감 기온 : \(listInfo.main.feelsLike)\(tempUnit.expression)" - maximumTemperatureLable.text = "최고 기온 : \(listInfo.main.tempMax)\(tempUnit.expression)" - minimumTemperatureLable.text = "최저 기온 : \(listInfo.main.tempMin)\(tempUnit.expression)" + guard let temperature = temperature else {return} + temperatureLabel.text = "현재 기온 : \(temperature.getTemperature(temp: listInfo.main.temp))\(temperature.unit)" + feelsLikeLabel.text = "체감 기온 : \(listInfo.main.feelsLike)\(temperature.unit)" + maximumTemperatureLable.text = "최고 기온 : \(listInfo.main.tempMax)\(temperature.unit)" + minimumTemperatureLable.text = "최저 기온 : \(listInfo.main.tempMin)\(temperature.unit)" popLabel.text = "강수 확률 : \(listInfo.main.pop * 100)%" humidityLabel.text = "습도 : \(listInfo.main.humidity)%" @@ -86,8 +87,8 @@ class WeatherDetailViewController: UIViewController, WeatherDetailViewDelegate, self.cityInfo = cityInfo } - func setTempUnit(tempUnit: TempUnit) { - self.tempUnit = tempUnit + func setTemperature(temperature: Temperature) { + self.temperature = temperature } func showDetailViewController(on navigationController: UINavigationController?) { diff --git a/WeatherForecast/WeatherForecast/WeatherViewController.swift b/WeatherForecast/WeatherForecast/WeatherViewController.swift index f9145d2..5c2ad1d 100644 --- a/WeatherForecast/WeatherForecast/WeatherViewController.swift +++ b/WeatherForecast/WeatherForecast/WeatherViewController.swift @@ -10,13 +10,13 @@ import UIKit protocol WeatherDetailPresentable { func setWeatherForecastInfo(weatherForecaseInfo: WeatherForecastInfo?) func setCityInfo(cityInfo: City?) - func setTempUnit(tempUnit: TempUnit) + func setTemperature(temperature: Temperature) func showDetailViewController(on navigationController: UINavigationController?) } class WeatherViewController: UIViewController, WeatherViewDelegate { private var weatherJSON: WeatherJSON? - private var tempUnit: TempUnit = .metric + private var temperature: Temperature private let dateFormatter: DateFormatter = { let formatter: DateFormatter = DateFormatter() formatter.locale = .init(identifier: "ko_KR") @@ -27,9 +27,10 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { private let presentable: WeatherDetailPresentable private let imageManager: ImageManager - init(presentable: WeatherDetailPresentable, imageManager: ImageManager) { + init(presentable: WeatherDetailPresentable, imageManager: ImageManager, temperature: Temperature = MetricTemperature()) { self.presentable = presentable self.imageManager = imageManager + self.temperature = temperature super.init(nibName: nil, bundle: nil) } @@ -76,8 +77,10 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { } func changeTempUnit() { - tempUnit = tempUnit == .imperial ? .metric : .imperial - navigationItem.rightBarButtonItem?.title = tempUnit == .imperial ? "화씨" : "섭씨" + temperature = temperature as? MetricTemperature == nil ? MetricTemperature() : ImperialTemperature() + navigationItem.rightBarButtonItem?.title = temperature.unitString +// tempUnit = tempUnit == .imperial ? .metric : .imperial +// navigationItem.rightBarButtonItem?.title = tempUnit == .imperial ? "화씨" : "섭씨" } func refresh() { @@ -102,7 +105,8 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { cell.weatherLabel.text = weatherForecastInfo.weather.main cell.descriptionLabel.text = weatherForecastInfo.weather.description - cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" + cell.temperatureLabel.text = "\(temperature.getTemperature(temp: weatherForecastInfo.main.temp))\(temperature.unit)" +// cell.temperatureLabel.text = "\(weatherForecastInfo.main.temp)\(tempUnit.expression)" cell.dateLabel.text = convertTimeToDateString(timeInterval: weatherForecastInfo.dt) setImageFromURL(icon: weatherForecastInfo.weather.icon, imageView: cell.weatherIcon) @@ -114,7 +118,7 @@ class WeatherViewController: UIViewController, WeatherViewDelegate { presentable.setWeatherForecastInfo(weatherForecaseInfo: weatherJSON?.weatherForecast[indexPath.row]) presentable.setCityInfo(cityInfo: weatherJSON?.city) - presentable.setTempUnit(tempUnit: tempUnit) + presentable.setTemperature(temperature: temperature) presentable.showDetailViewController(on: navigationController) } }