diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast.xcodeproj/project.pbxproj b/Jiwon's Weather Forecast/Jiwon's Weather Forecast.xcodeproj/project.pbxproj index b0ea9ee..0625466 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast.xcodeproj/project.pbxproj +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ 2C63B9CD2B067B4C009C6824 /* WeatherService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C63B9CC2B067B4C009C6824 /* WeatherService.swift */; }; 2C63B9D12B067FE0009C6824 /* WeatherBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C63B9D02B067FE0009C6824 /* WeatherBody.swift */; }; 2C9D36622AE8E3870033C284 /* DetailBottomBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9D36612AE8E3870033C284 /* DetailBottomBar.swift */; }; + 2C9F912B2B24B8C00028121F /* DetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F912A2B24B8C00028121F /* DetailTableViewCell.swift */; }; + 2C9F912D2B24C1A60028121F /* CityData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F912C2B24C1A60028121F /* CityData.swift */; }; 2CC2ABB22AF6956E00418A7C /* ListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2ABB12AF6956E00418A7C /* ListHeaderView.swift */; }; 2CC2ABB42AF6966000418A7C /* ListTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2ABB32AF6966000418A7C /* ListTableView.swift */; }; 2CC2ABB62AF697B300418A7C /* ListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC2ABB52AF697B300418A7C /* ListTableViewCell.swift */; }; @@ -42,7 +44,6 @@ 2CEAFFE82AE0268200BB21C0 /* DetailEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEAFFE72AE0268200BB21C0 /* DetailEnum.swift */; }; 2CEAFFEA2AE0281A00BB21C0 /* DetailData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEAFFE92AE0281A00BB21C0 /* DetailData.swift */; }; 2CF5DB152AFB8A1400137423 /* DetailCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF5DB142AFB8A1400137423 /* DetailCollectionViewCell.swift */; }; - 2CF5DB172AFB8C6C00137423 /* DetailTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF5DB162AFB8C6C00137423 /* DetailTableView.swift */; }; 2CF5DB192AFB98C000137423 /* DetailLiveCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF5DB182AFB98C000137423 /* DetailLiveCollectionViewCell.swift */; }; 2CF5DB1B2AFCB6FE00137423 /* DetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF5DB1A2AFCB6FE00137423 /* DetailHeaderView.swift */; }; 2CF5DB1D2AFCC72A00137423 /* DetailWeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CF5DB1C2AFCC72A00137423 /* DetailWeekView.swift */; }; @@ -75,6 +76,8 @@ 2C63B9CC2B067B4C009C6824 /* WeatherService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherService.swift; sourceTree = ""; }; 2C63B9D02B067FE0009C6824 /* WeatherBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBody.swift; sourceTree = ""; }; 2C9D36612AE8E3870033C284 /* DetailBottomBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailBottomBar.swift; sourceTree = ""; }; + 2C9F912A2B24B8C00028121F /* DetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTableViewCell.swift; sourceTree = ""; }; + 2C9F912C2B24C1A60028121F /* CityData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityData.swift; sourceTree = ""; }; 2CC2ABB12AF6956E00418A7C /* ListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListHeaderView.swift; sourceTree = ""; }; 2CC2ABB32AF6966000418A7C /* ListTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTableView.swift; sourceTree = ""; }; 2CC2ABB52AF697B300418A7C /* ListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTableViewCell.swift; sourceTree = ""; }; @@ -86,7 +89,6 @@ 2CEAFFE72AE0268200BB21C0 /* DetailEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEnum.swift; sourceTree = ""; }; 2CEAFFE92AE0281A00BB21C0 /* DetailData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailData.swift; sourceTree = ""; }; 2CF5DB142AFB8A1400137423 /* DetailCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCollectionViewCell.swift; sourceTree = ""; }; - 2CF5DB162AFB8C6C00137423 /* DetailTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTableView.swift; sourceTree = ""; }; 2CF5DB182AFB98C000137423 /* DetailLiveCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailLiveCollectionViewCell.swift; sourceTree = ""; }; 2CF5DB1A2AFCB6FE00137423 /* DetailHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailHeaderView.swift; sourceTree = ""; }; 2CF5DB1C2AFCC72A00137423 /* DetailWeekView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailWeekView.swift; sourceTree = ""; }; @@ -198,6 +200,7 @@ children = ( 2C63B9CC2B067B4C009C6824 /* WeatherService.swift */, 2C63B9D02B067FE0009C6824 /* WeatherBody.swift */, + 2C9F912C2B24C1A60028121F /* CityData.swift */, ); path = Network; sourceTree = ""; @@ -218,7 +221,7 @@ 2C9D36612AE8E3870033C284 /* DetailBottomBar.swift */, 2CEAFFDF2ADFC6BD00BB21C0 /* DetailView.swift */, 2CF5DB142AFB8A1400137423 /* DetailCollectionViewCell.swift */, - 2CF5DB162AFB8C6C00137423 /* DetailTableView.swift */, + 2C9F912A2B24B8C00028121F /* DetailTableViewCell.swift */, 2CF5DB1A2AFCB6FE00137423 /* DetailHeaderView.swift */, 2CEAFFE52ADFD34B00BB21C0 /* DetailLiveView.swift */, 2CF5DB182AFB98C000137423 /* DetailLiveCollectionViewCell.swift */, @@ -370,7 +373,6 @@ 2CEAFFE22ADFC6CF00BB21C0 /* DetailViewController.swift in Sources */, 2CF5DB152AFB8A1400137423 /* DetailCollectionViewCell.swift in Sources */, 2CEAFFE02ADFC6BD00BB21C0 /* DetailView.swift in Sources */, - 2CF5DB172AFB8C6C00137423 /* DetailTableView.swift in Sources */, 2C2EFE322ADD5E7E00B2A2EC /* ListViewController.swift in Sources */, 2CF5DB1D2AFCC72A00137423 /* DetailWeekView.swift in Sources */, 2CF5DB1B2AFCB6FE00137423 /* DetailHeaderView.swift in Sources */, @@ -385,8 +387,10 @@ 2CEAFFEA2AE0281A00BB21C0 /* DetailData.swift in Sources */, 2C63B9CD2B067B4C009C6824 /* WeatherService.swift in Sources */, 2CEAFFE82AE0268200BB21C0 /* DetailEnum.swift in Sources */, + 2C9F912B2B24B8C00028121F /* DetailTableViewCell.swift in Sources */, 2C528C252ADD96DE00383C90 /* UIFont+.swift in Sources */, 2C0069D52ADD6B6D00B933A2 /* UICollectionViewCell+.swift in Sources */, + 2C9F912D2B24C1A60028121F /* CityData.swift in Sources */, 2C0069CF2ADD6AF300B933A2 /* String+.swift in Sources */, 2C528C1F2ADD940100383C90 /* ListView.swift in Sources */, 2CF5DB1F2AFCC8E800137423 /* DetailEnum2.swift in Sources */, diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Image.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Image.swift index feaa2fc..5038719 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Image.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Image.swift @@ -29,5 +29,4 @@ public enum Image { //MARK: - WeatherImage static let weatherLarge = UIImage(named: "weatherLarge")! static let weatherSmall = UIImage(named: "weatherSmall")! - } diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/CityData.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/CityData.swift new file mode 100644 index 0000000..cbcce35 --- /dev/null +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/CityData.swift @@ -0,0 +1,10 @@ +// +// CityData.swift +// Jiwon's Weather Forecast +// +// Created by 신지원 on 12/10/23. +// + +import Foundation + +let Cities = ["gongju", "gwangju", "gumi", "gunsan", "daegu", "daejeon", "mokpo", "busan", "seosan", "seoul", "sokcho", "suwon", "suncheon", "ulsan", "iksan", "jeonju", "jeju", "cheonan", "cheongju", "chuncheon"] diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/WeatherService.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/WeatherService.swift index da18709..9975002 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/WeatherService.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Network/WeatherService.swift @@ -34,7 +34,6 @@ class WeatherService { do { let decodedData = try JSONDecoder().decode(Weathers.self, from: data) -// print(decodedData) completion(decodedData, nil) } catch { completion(nil, error) diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailCollectionViewCell.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailCollectionViewCell.swift index b388556..a8e2715 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailCollectionViewCell.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailCollectionViewCell.swift @@ -13,15 +13,19 @@ import Then class DetailCollectionViewCell: UICollectionViewCell { // MARK: - Properties + public var weatherDummy: [Weathers] = [] // MARK: - UI Components let detailBackImageView = UIImageView() - let detailTableView = DetailTableView() + let detailTableView = UITableView() // MARK: - Life Cycle override init(frame: CGRect) { super.init(frame: frame) + register() + delegate() + cellStyle() hierarchy() layout() @@ -31,10 +35,14 @@ class DetailCollectionViewCell: UICollectionViewCell { fatalError("init(coder:) has not been implemented") } -// private func delegate() { -// detailTableView.delegate = self -// detailTableView.dataSource = self -// } + private func register() { + detailTableView.register(DetailTableViewCell.self, forCellReuseIdentifier: DetailTableViewCell.cellIdentifier) + } + + private func delegate() { + detailTableView.delegate = self + detailTableView.dataSource = self + } private func cellStyle() { self.backgroundColor = UIColor(hex: 0x2A3040) @@ -61,29 +69,21 @@ class DetailCollectionViewCell: UICollectionViewCell { $0.edges.equalToSuperview() } } +} + +extension DetailCollectionViewCell: UITableViewDelegate {} +extension DetailCollectionViewCell: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } - func dataBind(tag : Int){ - detailTableView.detailHeaderView.detailPlaceLabel.text = listData[tag].place - detailTableView.detailHeaderView.detailWeatherLabel.text = listData[tag].weather - detailTableView.detailHeaderView.detailCurrentTemLabel.text = listData[tag].currentTem + "°" - detailTableView.detailHeaderView.detailHighTemLabel.text = "최고:" + listData[tag].highTem + "°" - detailTableView.detailHeaderView.detailLowTemLabel.text = "최저:" + listData[tag].lowTem + "°" + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: DetailTableViewCell.identifier, for: indexPath) as? DetailTableViewCell else { return DetailTableViewCell() } + cell.dataBind() + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 1300.0 } } -// -//extension DetailCollectionViewCell : UITableViewDelegate {} -//extension DetailCollectionViewCell : UITableViewDataSource { -// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { -// return 1 -// } -// -// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { -// guard let cell = UITableViewCell() -// return cell -// } -// -// func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { -// <#code#> -// } -// -//} diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveCollectionViewCell.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveCollectionViewCell.swift index d362838..3db1681 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveCollectionViewCell.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveCollectionViewCell.swift @@ -9,6 +9,9 @@ import UIKit class DetailLiveCollectionViewCell: UICollectionViewCell { + // MARK: - Properties + static let identifier: String = "DetailLiveCollectionViewCell" + // MARK: - UI Components var detailTime = UILabel() var detailIcon = UIImageView() diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveView.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveView.swift index 78347e8..d622083 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveView.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveView.swift @@ -42,7 +42,7 @@ class DetailLiveView: UIView { } private func register() { - detailLiveCollectionView.register(DetailLiveCollectionViewCell.self, forCellWithReuseIdentifier: DetailLiveCollectionViewCell.cellIdentifier) + detailLiveCollectionView.register(DetailLiveCollectionViewCell.self, forCellWithReuseIdentifier: DetailLiveCollectionViewCell.identifier) } private func style() { @@ -54,7 +54,7 @@ class DetailLiveView: UIView { } detailLiveLabel.do { - $0.text = "08:00~09:00에 강우 상태가, 18:00에 한때 흐린 상태가 예상됩니다." + $0.text = "From 08:00 to 09:00 Rainy conditions are expected" $0.font = .SFPro(.regular, size: 18) $0.textColor = .white $0.numberOfLines = 0 @@ -112,14 +112,13 @@ extension DetailLiveView : UICollectionViewDelegateFlowLayout { } } extension DetailLiveView : UICollectionViewDelegate {} - extension DetailLiveView : UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return detailLiveData.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailLiveCollectionViewCell.cellIdentifier, for: indexPath) as? DetailLiveCollectionViewCell else { return UICollectionViewCell() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailLiveCollectionViewCell.identifier, for: indexPath) as? DetailLiveCollectionViewCell else { return UICollectionViewCell() } cell.dataBind(tag: indexPath.row) return cell } diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailTableView.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailTableViewCell.swift similarity index 51% rename from Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailTableView.swift rename to Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailTableViewCell.swift index 3e532d1..16be876 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailTableView.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailTableViewCell.swift @@ -1,26 +1,29 @@ // -// DetailTableView.swift +// DetailTableViewCell.swift // Jiwon's Weather Forecast // -// Created by 신지원 on 11/8/23. +// Created by 신지원 on 12/10/23. // import UIKit -class DetailTableView: UITableView { +import SnapKit +import Then - // MARK: - Properties - var num = 0 +class DetailTableViewCell: UITableViewCell { + // MARK: - Properties + public var weatherDummy: [Weathers] = [] + // MARK: - UI Components let detailHeaderView = DetailHeaderView() let detailLiveView = DetailLiveView() let detailWeekView = DetailWeekView() - // MARK: - Life Cycle - override init(frame: CGRect, style: UITableView.Style) { - super.init(frame: frame, style: style) - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setStyle() hierarchy() layout() } @@ -28,6 +31,15 @@ class DetailTableView: UITableView { required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + private func setStyle() { + self.do { + $0.backgroundColor = .clear + $0.contentView.isHidden = true + $0.selectedBackgroundView?.isHidden = true + } + } + private func hierarchy() { self.addSubviews(detailHeaderView, detailLiveView, detailWeekView) } @@ -50,7 +62,15 @@ class DetailTableView: UITableView { $0.top.equalTo(detailLiveView.snp.bottom).offset(10) $0.centerX.equalToSuperview() $0.leading.equalTo(20) -// $0.height.equalTo(678) + $0.height.equalTo(600) } } + + func dataBind(){ +// detailHeaderView.detailPlaceLabel.text = weatherdummy[0].name +// detailHeaderView.detailWeatherLabel.text = weatherdummy[0].weather.first?.description +// detailHeaderView.detailCurrentTemLabel.text = String(weatherdummy[0].main.temp) + "°" +// detailHeaderView.detailHighTemLabel.text = "최고:" + String(weatherdummy[0].main.temp_max) + "°" +// detailHeaderView.detailLowTemLabel.text = "최저:" + String(weatherdummy[0].main.temp_min) + "°" + } } diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekCollectionViewCell.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekCollectionViewCell.swift index 0ce39c9..cb0e847 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekCollectionViewCell.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekCollectionViewCell.swift @@ -9,6 +9,9 @@ import UIKit class DetailWeekCollectionViewCell: UICollectionViewCell { + // MARK: - Properties + static let identifier: String = "DetailWeekCollectionViewCell" + // MARK: - UI Components var detailDay = UILabel() var detailIcon = UIImageView() @@ -114,8 +117,7 @@ class DetailWeekCollectionViewCell: UICollectionViewCell { var min = (Double)(detailWeekData[tag].lowTem - detailWeekDataMin) / (Double)(detailWeekDataMax - detailWeekDataMin) * 100 var max = (Double)(detailWeekDataMax - detailWeekData[tag].highTem) / (Double)(detailWeekDataMax - detailWeekDataMin) * 100 - print(min) - print(max) + empty1.snp.makeConstraints() { $0.top.equalTo(detailGradientTemp.snp.top) $0.leading.equalTo(detailGradientTemp.snp.leading) diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekView.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekView.swift index 02c6d0c..56c192f 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekView.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekView.swift @@ -43,7 +43,7 @@ class DetailWeekView: UIView { } private func register() { - detailWeekCollectionView.register(DetailWeekCollectionViewCell.self, forCellWithReuseIdentifier: DetailWeekCollectionViewCell.cellIdentifier) + detailWeekCollectionView.register(DetailWeekCollectionViewCell.self, forCellWithReuseIdentifier: DetailWeekCollectionViewCell.identifier) } private func style() { @@ -55,7 +55,7 @@ class DetailWeekView: UIView { } detailWeekLabel.do { - $0.text = "10일간의 일기예보" + $0.text = "10-DAY FORECAST" $0.font = .SFPro(.thin, size: 15) $0.textColor = UIColor(white: 1.0, alpha: 0.5) $0.numberOfLines = 0 @@ -107,7 +107,7 @@ extension DetailWeekView : UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailWeekCollectionViewCell.cellIdentifier, for: indexPath) as? DetailWeekCollectionViewCell else { return UICollectionViewCell() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailWeekCollectionViewCell.identifier, for: indexPath) as? DetailWeekCollectionViewCell else { return UICollectionViewCell() } cell.dataBind(tag: indexPath.row) return cell } diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/ViewController/DetailViewController.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/ViewController/DetailViewController.swift index 4783337..5453b52 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/ViewController/DetailViewController.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/ViewController/DetailViewController.swift @@ -13,11 +13,14 @@ import Then class DetailViewController: UIViewController { // MARK: - Properties + var index: Int = 0 + public var weatherDummy : [Weathers] = [] + public var detailTag = Int() // MARK: - UI Components let rootView = DetailView() - + // MARK: - Life Cycle override func loadView() { self.view = rootView @@ -29,8 +32,6 @@ class DetailViewController: UIViewController { gesture() target() delegate() - -// responseData(forCity: "Seoul") } // MARK: - Custom Method @@ -78,12 +79,12 @@ extension DetailViewController : UICollectionViewDelegate { extension DetailViewController : UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return listData.count + return weatherDummy.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DetailCollectionViewCell.cellIdentifier, for: indexPath) as? DetailCollectionViewCell else { return UICollectionViewCell() } - cell.dataBind(tag: indexPath.row) + cell.weatherDummy = self.weatherDummy return cell } } diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/View/ListTableViewCell.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/View/ListTableViewCell.swift index 4a9727c..38ff36f 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/View/ListTableViewCell.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/View/ListTableViewCell.swift @@ -10,8 +10,14 @@ import UIKit import SnapKit import Then +protocol ListTableViewCellDelegate : AnyObject { + func listBtnTap(cell: UITableViewCell) +} + class ListTableViewCell: UITableViewCell { + weak var delegate: ListTableViewCellDelegate? + // MARK: - UI Components var listButton = UIButton() @@ -28,6 +34,7 @@ class ListTableViewCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) + self.contentView.isHidden = true cellStyle() hierarchy() layout() @@ -42,8 +49,13 @@ class ListTableViewCell: UITableViewCell { func cellStyle() { self.backgroundColor = .black + self.selectedBackgroundView?.isHidden = true - listButton.setBackgroundImage(Image.weatherSmall, for: .normal) + listButton.do { + $0.setBackgroundImage(Image.weatherSmall, for: .normal) + $0.addTarget(self, action: #selector(listBtnTap), for: .touchUpInside) + $0.isUserInteractionEnabled = true + } placeLabel.do { $0.font = .SFPro(.bold, size: 24) @@ -149,4 +161,9 @@ class ListTableViewCell: UITableViewCell { let localTime = dateFormatter.string(from: Date()) return localTime } + + @objc + private func listBtnTap() { + delegate?.listBtnTap(cell: self) + } } diff --git a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/ViewController/ListViewController.swift b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/ViewController/ListViewController.swift index b3f75e4..804bb21 100644 --- a/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/ViewController/ListViewController.swift +++ b/Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherList/ViewController/ListViewController.swift @@ -13,11 +13,12 @@ import Then class ListViewController: UIViewController { // MARK: - Properties - let cities = ["gongju", "gwangju", "gumi", "gunsan", "daegu", "daejeon", "mokpo", "busan", "seosan", "seoul", "sokcho", "suwon", "suncheon", "ulsan", "iksan", "jeonju", "jeju", "cheonan", "cheongju", "chuncheon"] + private var weatherDummy: [Weathers] = [] // MARK: - UI Components private let rootView = ListView() - private var weatherdummy: [Weathers] = [] + public var detailPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) + lazy var detailVCs: [DetailViewController] = [] func getWeathers(cities : [String]) { for i in cities { @@ -26,7 +27,7 @@ class ListViewController: UIViewController { print("Error: \(error)") } else if let weather = weather { DispatchQueue.main.async { // 메인 스레드로 전환 - self.weatherdummy.append(weather) + self.weatherDummy.append(weather) self.loadData() } } @@ -46,7 +47,14 @@ class ListViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - getWeathers(cities: cities) + let detailVC = getDetailVC(withIndex: 0) + detailPageViewController.setViewControllers([detailVC], direction: .forward, animated: true, completion: nil) + + self.addChild(detailPageViewController) +// self.view.addSubview(detailPageViewController.view) + detailPageViewController.didMove(toParent: self) + + getWeathers(cities: Cities) gesture() target() @@ -64,24 +72,27 @@ class ListViewController: UIViewController { } private func delegate() { + detailPageViewController.dataSource = self + detailPageViewController.delegate = self rootView.listTableView.delegate = self rootView.listTableView.dataSource = self + // ListTable + } + + func getDetailVC(withIndex index: Int) -> DetailViewController { + let detailVC = DetailViewController() + detailVC.index = index + print("🥳\(index)") + return detailVC } //MARK: - Action Method + @objc - func listBtnTap(_ sender: UIButton) { - let tag = sender.tag - print("listTap with number: \(tag)") - - // let detailVC = DetailViewController() - // detailVC.rootView.detailInfoView.detailWeatherLabel.text = listData[tag].weather - // detailVC.rootView.detailInfoView.detailHighTemLabel.text = "최고:" + listData[tag].highTem + "°" - // detailVC.rootView.detailInfoView.detailLowTemLabel.text = "최저:" + listData[tag].lowTem + "°" - // detailVC.rootView.detailInfoView.detailCurrentTemLabel.text = listData[tag].currentTem + "°" - // detailVC.rootView.detailInfoView.detailPlaceLabel.text = listData[tag].place - // - // self.navigationController?.pushViewController(detailVC, animated: true) + private func buttonTap(_ sender: UIButton) { + let index = sender.tag + let detailVC = getDetailVC(withIndex : index) + detailPageViewController.setViewControllers([detailVC], direction: .forward, animated: true, completion: nil) } } @@ -103,75 +114,109 @@ extension ListViewController : UITableViewDelegate { extension ListViewController : UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return weatherdummy.count + return weatherDummy.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { guard let cell = tableView.dequeueReusableCell(withIdentifier: ListTableViewCell.cellIdentifier, for: indexPath) as? ListTableViewCell else { return UITableViewCell() } - cell.dataBind(weatherdummy[indexPath.row]) + cell.dataBind(weatherDummy[indexPath.row]) return cell } } -//// MARK: - UISearchBarDelegate -//extension ListViewController: UISearchBarDelegate { -// func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { -// print("cancel") -// rootView.listWeatherSearchBar.searchTextField.text = "" -// rootView.endEditing(true) -// } -// -// func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { -// print("눌렀다💛") -// rootView.endEditing(true) -// -// } -// -// func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { -// rootView.listWeatherLabel.isHidden = true -// -// //이동 위치 구체적 설정 + 키보드 비동기처리 해야 함 -// rootView.listScrollView.setContentOffset(CGPoint(x: 0 ,y: 80), animated: true) -// rootView.listWeatherSearchBar.showsCancelButton = true -// } -// -// func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { -// rootView.listWeatherLabel.isHidden = false -// rootView.listScrollView.setContentOffset(CGPoint(x: 0 ,y: 0), animated: true) -// rootView.listWeatherSearchBar.showsCancelButton = false -// } -// -// func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { -// if searchText.isEmpty { -// -// } -// else { -// var filter = listData.filter {$0.place.lowercased().contains(searchText.lowercased())} -// print("\(filter)💗") -// -// rootView.listStackView.arrangedSubviews.forEach { -// rootView.listStackView.removeArrangedSubview($0) -// rootView.listStackView.removeFromSuperview() -// } -// -// var i = 0 -// for reloadList in filter { -// let reloadButton = ListBaseButton() -// reloadButton.do { -// $0.placeLabel.text = listData[i].place -// $0.timeLabel.text = listData[i].time -// $0.weatherLabel.text = listData[i].weather -// $0.currentTemLabel.text = listData[i].currentTem + "°" -// $0.highTemLabel.text = "최고:" + listData[i].highTem + "°" -// $0.lowTemLabel.text = "최저:" + listData[i].lowTem + "°" -// $0.tag = i -// -// rootView.listStackView.addArrangedSubview(reloadButton) -// } -// i = i+1 -// } -// rootView.listScrollView.reloadInputViews() +extension ListViewController: ListTableViewCellDelegate { + func listBtnTap(cell: UITableViewCell) { + + print("💖") + +// if let indexPath = rootView.listTableView.indexPath(for: cell) { +// // 여기서 indexPath를 사용하여 필요한 작업을 수행합니다. +// print("Button tapped in cell at section \(indexPath.section), row \(indexPath.row)") // } -// } -//} + + // let detailVC = DetailViewController() + // detailVC.weatherDummy = self.weatherDummy + // detailVC.detailTag = tag + // self.navigationController?.pushViewController(detailVC, animated: true) + } +} + +extension ListViewController: UIPageViewControllerDataSource, UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + if let currentIndex = detailVCs.firstIndex(of: viewController as! DetailViewController), currentIndex > 0 { + return detailVCs[currentIndex - 1] + } + return nil + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + if let currentIndex = detailVCs.firstIndex(of: viewController as! DetailViewController), currentIndex < detailVCs.count - 1 { + return detailVCs[currentIndex + 1] + } + return nil + } +} + + +//// MARK: - UISearchBarDelegate +extension ListViewController: UISearchBarDelegate { + func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + print("cancel") + rootView.listWeatherSearchBar.searchTextField.text = "" + rootView.endEditing(true) + } + + func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + print("눌렀다💛") + rootView.endEditing(true) + + } + + func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { + rootView.listWeatherLabel.isHidden = true + + //이동 위치 구체적 설정 + 키보드 비동기처리 해야 함 + rootView.listScrollView.setContentOffset(CGPoint(x: 0 ,y: 80), animated: true) + rootView.listWeatherSearchBar.showsCancelButton = true + } + + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + rootView.listWeatherLabel.isHidden = false + rootView.listScrollView.setContentOffset(CGPoint(x: 0 ,y: 0), animated: true) + rootView.listWeatherSearchBar.showsCancelButton = false + } + + func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { + if searchText.isEmpty { + + } + else { + var filter = listData.filter {$0.place.lowercased().contains(searchText.lowercased())} + print("\(filter)💗") + + rootView.listStackView.arrangedSubviews.forEach { + rootView.listStackView.removeArrangedSubview($0) + rootView.listStackView.removeFromSuperview() + } + + var i = 0 + for reloadList in filter { + let reloadButton = ListBaseButton() + reloadButton.do { + $0.placeLabel.text = listData[i].place + $0.timeLabel.text = listData[i].time + $0.weatherLabel.text = listData[i].weather + $0.currentTemLabel.text = listData[i].currentTem + "°" + $0.highTemLabel.text = "최고:" + listData[i].highTem + "°" + $0.lowTemLabel.text = "최저:" + listData[i].lowTem + "°" + $0.tag = i + + rootView.listStackView.addArrangedSubview(reloadButton) + } + i = i+1 + } + rootView.listScrollView.reloadInputViews() + } + } +} diff --git a/Weather_App/Weather_App.xcodeproj/project.pbxproj b/Weather_App/Weather_App.xcodeproj/project.pbxproj new file mode 100644 index 0000000..1d1c7a0 --- /dev/null +++ b/Weather_App/Weather_App.xcodeproj/project.pbxproj @@ -0,0 +1,543 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 2C6DE2A02B274C6400DB9044 /* DetailPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE29F2B274C6400DB9044 /* DetailPageViewController.swift */; }; + 2C6DE2A22B274CF600DB9044 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2A12B274CF600DB9044 /* DetailViewController.swift */; }; + 2C6DE2A42B27512000DB9044 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2A32B27512000DB9044 /* DetailView.swift */; }; + 2C6DE2A62B275F4B00DB9044 /* DetailBottomBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2A52B275F4B00DB9044 /* DetailBottomBar.swift */; }; + 2C6DE2AA2B27631500DB9044 /* DetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2A92B27631500DB9044 /* DetailTableViewCell.swift */; }; + 2C6DE2AC2B27633E00DB9044 /* DetailHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2AB2B27633E00DB9044 /* DetailHeaderView.swift */; }; + 2C6DE2AE2B27634D00DB9044 /* DetailLiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2AD2B27634D00DB9044 /* DetailLiveView.swift */; }; + 2C6DE2B02B27635000DB9044 /* DetailLiveCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2AF2B27635000DB9044 /* DetailLiveCollectionViewCell.swift */; }; + 2C6DE2B22B27635200DB9044 /* DetailWeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2B12B27635200DB9044 /* DetailWeekView.swift */; }; + 2C6DE2B42B27635500DB9044 /* DetailWeekCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2B32B27635500DB9044 /* DetailWeekCollectionViewCell.swift */; }; + 2C6DE2B62B27635B00DB9044 /* DetailWeekGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2B52B27635B00DB9044 /* DetailWeekGradientView.swift */; }; + 2C6DE2B92B276A2300DB9044 /* DetailData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2B82B276A2300DB9044 /* DetailData.swift */; }; + 2C6DE2BB2B276A3E00DB9044 /* DetailEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2BA2B276A3E00DB9044 /* DetailEnum.swift */; }; + 2C6DE2BD2B276A5900DB9044 /* DetailEnum2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2BC2B276A5900DB9044 /* DetailEnum2.swift */; }; + 2C6DE2BF2B276A7900DB9044 /* WeatherService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2BE2B276A7900DB9044 /* WeatherService.swift */; }; + 2C6DE2C12B276A8E00DB9044 /* WeatherBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2C02B276A8E00DB9044 /* WeatherBody.swift */; }; + 2C6DE2C32B276AA300DB9044 /* CityData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6DE2C22B276AA300DB9044 /* CityData.swift */; }; + 2C9F915B2B2700800028121F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F915A2B2700800028121F /* AppDelegate.swift */; }; + 2C9F915D2B2700800028121F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F915C2B2700800028121F /* SceneDelegate.swift */; }; + 2C9F915F2B2700800028121F /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F915E2B2700800028121F /* ListViewController.swift */; }; + 2C9F91642B2700830028121F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C9F91632B2700830028121F /* Assets.xcassets */; }; + 2C9F91702B2701A70028121F /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 2C9F916F2B2701A70028121F /* Then */; }; + 2C9F91732B2701B10028121F /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 2C9F91722B2701B10028121F /* SnapKit */; }; + 2C9F91752B2701B10028121F /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 2C9F91742B2701B10028121F /* SnapKit-Dynamic */; }; + 2C9F91782B2702210028121F /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F91772B2702210028121F /* ListView.swift */; }; + 2C9F917A2B2702E70028121F /* ListTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F91792B2702E70028121F /* ListTableView.swift */; }; + 2C9F917C2B2703170028121F /* ListTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F917B2B2703170028121F /* ListTableViewHeader.swift */; }; + 2C9F917E2B2703DA0028121F /* ListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F917D2B2703DA0028121F /* ListTableViewCell.swift */; }; + 2C9F91802B27042F0028121F /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F917F2B27042F0028121F /* UIFont+.swift */; }; + 2C9F91832B2704590028121F /* SF-Pro-Display-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2C9F91822B2704590028121F /* SF-Pro-Display-Light.otf */; }; + 2C9F91852B27045D0028121F /* SF-Pro-Display-Thin.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2C9F91842B27045D0028121F /* SF-Pro-Display-Thin.otf */; }; + 2C9F91872B2704610028121F /* SF-Pro-Display-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2C9F91862B2704610028121F /* SF-Pro-Display-Bold.otf */; }; + 2C9F91892B2704660028121F /* SF-Pro-Display-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2C9F91882B2704660028121F /* SF-Pro-Display-Medium.otf */; }; + 2C9F918B2B27046A0028121F /* SF-Pro-Display-Regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = 2C9F918A2B2704690028121F /* SF-Pro-Display-Regular.otf */; }; + 2C9F918E2B2704D60028121F /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C9F918D2B2704D60028121F /* UIImage+.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2C6DE29F2B274C6400DB9044 /* DetailPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailPageViewController.swift; sourceTree = ""; }; + 2C6DE2A12B274CF600DB9044 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; + 2C6DE2A32B27512000DB9044 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; + 2C6DE2A52B275F4B00DB9044 /* DetailBottomBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailBottomBar.swift; sourceTree = ""; }; + 2C6DE2A92B27631500DB9044 /* DetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTableViewCell.swift; sourceTree = ""; }; + 2C6DE2AB2B27633E00DB9044 /* DetailHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailHeaderView.swift; sourceTree = ""; }; + 2C6DE2AD2B27634D00DB9044 /* DetailLiveView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DetailLiveView.swift; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveView.swift"; sourceTree = ""; }; + 2C6DE2AF2B27635000DB9044 /* DetailLiveCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DetailLiveCollectionViewCell.swift; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailLiveCollectionViewCell.swift"; sourceTree = ""; }; + 2C6DE2B12B27635200DB9044 /* DetailWeekView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DetailWeekView.swift; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekView.swift"; sourceTree = ""; }; + 2C6DE2B32B27635500DB9044 /* DetailWeekCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DetailWeekCollectionViewCell.swift; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekCollectionViewCell.swift"; sourceTree = ""; }; + 2C6DE2B52B27635B00DB9044 /* DetailWeekGradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DetailWeekGradientView.swift; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Presentation/WeatherDetail/View/DetailWeekGradientView.swift"; sourceTree = ""; }; + 2C6DE2B82B276A2300DB9044 /* DetailData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailData.swift; sourceTree = ""; }; + 2C6DE2BA2B276A3E00DB9044 /* DetailEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEnum.swift; sourceTree = ""; }; + 2C6DE2BC2B276A5900DB9044 /* DetailEnum2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEnum2.swift; sourceTree = ""; }; + 2C6DE2BE2B276A7900DB9044 /* WeatherService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherService.swift; sourceTree = ""; }; + 2C6DE2C02B276A8E00DB9044 /* WeatherBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherBody.swift; sourceTree = ""; }; + 2C6DE2C22B276AA300DB9044 /* CityData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityData.swift; sourceTree = ""; }; + 2C9F91572B2700800028121F /* Weather_App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Weather_App.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 2C9F915A2B2700800028121F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 2C9F915C2B2700800028121F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 2C9F915E2B2700800028121F /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; + 2C9F91632B2700830028121F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 2C9F91682B2700830028121F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 2C9F91772B2702210028121F /* ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListView.swift; sourceTree = ""; }; + 2C9F91792B2702E70028121F /* ListTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTableView.swift; sourceTree = ""; }; + 2C9F917B2B2703170028121F /* ListTableViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTableViewHeader.swift; sourceTree = ""; }; + 2C9F917D2B2703DA0028121F /* ListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTableViewCell.swift; sourceTree = ""; }; + 2C9F917F2B27042F0028121F /* UIFont+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; + 2C9F91822B2704590028121F /* SF-Pro-Display-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Pro-Display-Light.otf"; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Fonts/SF-Pro-Display-Light.otf"; sourceTree = ""; }; + 2C9F91842B27045D0028121F /* SF-Pro-Display-Thin.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Pro-Display-Thin.otf"; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Fonts/SF-Pro-Display-Thin.otf"; sourceTree = ""; }; + 2C9F91862B2704610028121F /* SF-Pro-Display-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Pro-Display-Bold.otf"; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Fonts/SF-Pro-Display-Bold.otf"; sourceTree = ""; }; + 2C9F91882B2704660028121F /* SF-Pro-Display-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Pro-Display-Medium.otf"; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Fonts/SF-Pro-Display-Medium.otf"; sourceTree = ""; }; + 2C9F918A2B2704690028121F /* SF-Pro-Display-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "SF-Pro-Display-Regular.otf"; path = "../../../Jiwon's Weather Forecast/Jiwon's Weather Forecast/Constant/Fonts/SF-Pro-Display-Regular.otf"; sourceTree = ""; }; + 2C9F918D2B2704D60028121F /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 2C9F91542B2700800028121F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C9F91702B2701A70028121F /* Then in Frameworks */, + 2C9F91732B2701B10028121F /* SnapKit in Frameworks */, + 2C9F91752B2701B10028121F /* SnapKit-Dynamic in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2C6DE29E2B274BFA00DB9044 /* Detail */ = { + isa = PBXGroup; + children = ( + 2C6DE29F2B274C6400DB9044 /* DetailPageViewController.swift */, + 2C6DE2A52B275F4B00DB9044 /* DetailBottomBar.swift */, + 2C6DE2A12B274CF600DB9044 /* DetailViewController.swift */, + 2C6DE2A32B27512000DB9044 /* DetailView.swift */, + 2C6DE2A92B27631500DB9044 /* DetailTableViewCell.swift */, + 2C6DE2AB2B27633E00DB9044 /* DetailHeaderView.swift */, + 2C6DE2AD2B27634D00DB9044 /* DetailLiveView.swift */, + 2C6DE2AF2B27635000DB9044 /* DetailLiveCollectionViewCell.swift */, + 2C6DE2B12B27635200DB9044 /* DetailWeekView.swift */, + 2C6DE2B32B27635500DB9044 /* DetailWeekCollectionViewCell.swift */, + 2C6DE2B52B27635B00DB9044 /* DetailWeekGradientView.swift */, + ); + path = Detail; + sourceTree = ""; + }; + 2C6DE2B72B276A0100DB9044 /* Network */ = { + isa = PBXGroup; + children = ( + 2C6DE2B82B276A2300DB9044 /* DetailData.swift */, + 2C6DE2BA2B276A3E00DB9044 /* DetailEnum.swift */, + 2C6DE2BC2B276A5900DB9044 /* DetailEnum2.swift */, + 2C6DE2BE2B276A7900DB9044 /* WeatherService.swift */, + 2C6DE2C02B276A8E00DB9044 /* WeatherBody.swift */, + 2C6DE2C22B276AA300DB9044 /* CityData.swift */, + ); + path = Network; + sourceTree = ""; + }; + 2C9F914E2B2700800028121F = { + isa = PBXGroup; + children = ( + 2C9F91592B2700800028121F /* Weather_App */, + 2C9F91582B2700800028121F /* Products */, + ); + sourceTree = ""; + }; + 2C9F91582B2700800028121F /* Products */ = { + isa = PBXGroup; + children = ( + 2C9F91572B2700800028121F /* Weather_App.app */, + ); + name = Products; + sourceTree = ""; + }; + 2C9F91592B2700800028121F /* Weather_App */ = { + isa = PBXGroup; + children = ( + 2C9F915A2B2700800028121F /* AppDelegate.swift */, + 2C9F915C2B2700800028121F /* SceneDelegate.swift */, + 2C9F918C2B2704CE0028121F /* Image */, + 2C9F91812B2704500028121F /* Font */, + 2C6DE29E2B274BFA00DB9044 /* Detail */, + 2C9F91762B27020A0028121F /* List */, + 2C6DE2B72B276A0100DB9044 /* Network */, + 2C9F91682B2700830028121F /* Info.plist */, + ); + path = Weather_App; + sourceTree = ""; + }; + 2C9F91762B27020A0028121F /* List */ = { + isa = PBXGroup; + children = ( + 2C9F915E2B2700800028121F /* ListViewController.swift */, + 2C9F91772B2702210028121F /* ListView.swift */, + 2C9F91792B2702E70028121F /* ListTableView.swift */, + 2C9F917B2B2703170028121F /* ListTableViewHeader.swift */, + 2C9F917D2B2703DA0028121F /* ListTableViewCell.swift */, + ); + path = List; + sourceTree = ""; + }; + 2C9F91812B2704500028121F /* Font */ = { + isa = PBXGroup; + children = ( + 2C9F917F2B27042F0028121F /* UIFont+.swift */, + 2C9F91822B2704590028121F /* SF-Pro-Display-Light.otf */, + 2C9F91842B27045D0028121F /* SF-Pro-Display-Thin.otf */, + 2C9F91862B2704610028121F /* SF-Pro-Display-Bold.otf */, + 2C9F91882B2704660028121F /* SF-Pro-Display-Medium.otf */, + 2C9F918A2B2704690028121F /* SF-Pro-Display-Regular.otf */, + ); + path = Font; + sourceTree = ""; + }; + 2C9F918C2B2704CE0028121F /* Image */ = { + isa = PBXGroup; + children = ( + 2C9F91632B2700830028121F /* Assets.xcassets */, + 2C9F918D2B2704D60028121F /* UIImage+.swift */, + ); + path = Image; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 2C9F91562B2700800028121F /* Weather_App */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2C9F916B2B2700830028121F /* Build configuration list for PBXNativeTarget "Weather_App" */; + buildPhases = ( + 2C9F91532B2700800028121F /* Sources */, + 2C9F91542B2700800028121F /* Frameworks */, + 2C9F91552B2700800028121F /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Weather_App; + packageProductDependencies = ( + 2C9F916F2B2701A70028121F /* Then */, + 2C9F91722B2701B10028121F /* SnapKit */, + 2C9F91742B2701B10028121F /* SnapKit-Dynamic */, + ); + productName = Weather_App; + productReference = 2C9F91572B2700800028121F /* Weather_App.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 2C9F914F2B2700800028121F /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + 2C9F91562B2700800028121F = { + CreatedOnToolsVersion = 15.0.1; + }; + }; + }; + buildConfigurationList = 2C9F91522B2700800028121F /* Build configuration list for PBXProject "Weather_App" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 2C9F914E2B2700800028121F; + packageReferences = ( + 2C9F916E2B2701A70028121F /* XCRemoteSwiftPackageReference "Then" */, + 2C9F91712B2701B10028121F /* XCRemoteSwiftPackageReference "SnapKit" */, + ); + productRefGroup = 2C9F91582B2700800028121F /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 2C9F91562B2700800028121F /* Weather_App */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 2C9F91552B2700800028121F /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C9F91832B2704590028121F /* SF-Pro-Display-Light.otf in Resources */, + 2C9F91642B2700830028121F /* Assets.xcassets in Resources */, + 2C9F91872B2704610028121F /* SF-Pro-Display-Bold.otf in Resources */, + 2C9F91852B27045D0028121F /* SF-Pro-Display-Thin.otf in Resources */, + 2C9F918B2B27046A0028121F /* SF-Pro-Display-Regular.otf in Resources */, + 2C9F91892B2704660028121F /* SF-Pro-Display-Medium.otf in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 2C9F91532B2700800028121F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2C6DE2A62B275F4B00DB9044 /* DetailBottomBar.swift in Sources */, + 2C6DE2B62B27635B00DB9044 /* DetailWeekGradientView.swift in Sources */, + 2C6DE2A22B274CF600DB9044 /* DetailViewController.swift in Sources */, + 2C6DE2C32B276AA300DB9044 /* CityData.swift in Sources */, + 2C6DE2BB2B276A3E00DB9044 /* DetailEnum.swift in Sources */, + 2C6DE2A42B27512000DB9044 /* DetailView.swift in Sources */, + 2C6DE2AA2B27631500DB9044 /* DetailTableViewCell.swift in Sources */, + 2C6DE2BD2B276A5900DB9044 /* DetailEnum2.swift in Sources */, + 2C6DE2A02B274C6400DB9044 /* DetailPageViewController.swift in Sources */, + 2C9F917E2B2703DA0028121F /* ListTableViewCell.swift in Sources */, + 2C9F917C2B2703170028121F /* ListTableViewHeader.swift in Sources */, + 2C9F91782B2702210028121F /* ListView.swift in Sources */, + 2C6DE2AC2B27633E00DB9044 /* DetailHeaderView.swift in Sources */, + 2C6DE2B02B27635000DB9044 /* DetailLiveCollectionViewCell.swift in Sources */, + 2C6DE2C12B276A8E00DB9044 /* WeatherBody.swift in Sources */, + 2C9F915F2B2700800028121F /* ListViewController.swift in Sources */, + 2C9F915B2B2700800028121F /* AppDelegate.swift in Sources */, + 2C6DE2B92B276A2300DB9044 /* DetailData.swift in Sources */, + 2C9F91802B27042F0028121F /* UIFont+.swift in Sources */, + 2C9F917A2B2702E70028121F /* ListTableView.swift in Sources */, + 2C9F918E2B2704D60028121F /* UIImage+.swift in Sources */, + 2C6DE2BF2B276A7900DB9044 /* WeatherService.swift in Sources */, + 2C6DE2B22B27635200DB9044 /* DetailWeekView.swift in Sources */, + 2C9F915D2B2700800028121F /* SceneDelegate.swift in Sources */, + 2C6DE2AE2B27634D00DB9044 /* DetailLiveView.swift in Sources */, + 2C6DE2B42B27635500DB9044 /* DetailWeekCollectionViewCell.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 2C9F91692B2700830028121F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 2C9F916A2B2700830028121F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 2C9F916C2B2700830028121F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = L22KGAL4ND; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Weather_App/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shinjiwon.Weather-App"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 2C9F916D2B2700830028121F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = L22KGAL4ND; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Weather_App/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "shinjiwon.Weather-App"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 2C9F91522B2700800028121F /* Build configuration list for PBXProject "Weather_App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2C9F91692B2700830028121F /* Debug */, + 2C9F916A2B2700830028121F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2C9F916B2B2700830028121F /* Build configuration list for PBXNativeTarget "Weather_App" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2C9F916C2B2700830028121F /* Debug */, + 2C9F916D2B2700830028121F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 2C9F916E2B2701A70028121F /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; + 2C9F91712B2701B10028121F /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.6.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 2C9F916F2B2701A70028121F /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 2C9F916E2B2701A70028121F /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; + 2C9F91722B2701B10028121F /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 2C9F91712B2701B10028121F /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; + 2C9F91742B2701B10028121F /* SnapKit-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 2C9F91712B2701B10028121F /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = "SnapKit-Dynamic"; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 2C9F914F2B2700800028121F /* Project object */; +} diff --git a/Weather_App/Weather_App.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Weather_App/Weather_App.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Weather_App/Weather_App.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Weather_App/Weather_App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Weather_App/Weather_App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Weather_App/Weather_App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Weather_App/Weather_App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Weather_App/Weather_App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..f04a0b9 --- /dev/null +++ b/Weather_App/Weather_App.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit", + "state" : { + "revision" : "f222cbdf325885926566172f6f5f06af95473158", + "version" : "5.6.0" + } + }, + { + "identity" : "then", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devxoul/Then", + "state" : { + "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", + "version" : "3.0.0" + } + } + ], + "version" : 2 +} diff --git a/Weather_App/Weather_App/AppDelegate.swift b/Weather_App/Weather_App/AppDelegate.swift new file mode 100644 index 0000000..011dcef --- /dev/null +++ b/Weather_App/Weather_App/AppDelegate.swift @@ -0,0 +1,34 @@ +// +// AppDelegate.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Weather_App/Weather_App/Detail/DetailBottomBar.swift b/Weather_App/Weather_App/Detail/DetailBottomBar.swift new file mode 100644 index 0000000..4262b8e --- /dev/null +++ b/Weather_App/Weather_App/Detail/DetailBottomBar.swift @@ -0,0 +1,80 @@ +// +// DetailBottomBar.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import UIKit + +import SnapKit +import Then + +class DetailBottomBar: UIView { + + // MARK: - Properties + + // MARK: - UI Components + var detailMapButton = UIButton() + var detailListButton = UIButton() + let detailPageController = UIPageControl() + + // MARK: - Life Cycle + override init(frame: CGRect) { + super.init(frame: frame) + + style() + hierarchy() + layout() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Custom Method + private func style() { + self.backgroundColor = UIColor(hex: 0x2A3040) + + detailMapButton.do { + $0.setImage(Image.iconMap, for: .normal) + $0.isUserInteractionEnabled = true + } + + detailListButton.do { + $0.setImage(Image.iconList, for: .normal) + $0.isUserInteractionEnabled = true + } + + detailPageController.do { + $0.hidesForSinglePage = false + $0.numberOfPages = Cities.count + $0.pageIndicatorTintColor = .gray + $0.currentPageIndicatorTintColor = .white + } + } + + private func hierarchy() { + self.addSubviews(detailMapButton, detailListButton, detailPageController) + } + + private func layout() { + detailMapButton.snp.makeConstraints() { + $0.top.equalToSuperview().inset(4) + $0.leading.equalToSuperview().inset(10) + $0.size.equalTo(44) + } + + detailListButton.snp.makeConstraints() { + $0.top.equalToSuperview().inset(4) + $0.trailing.equalToSuperview().inset(10) + $0.size.equalTo(44) + } + + detailPageController.snp.makeConstraints() { + $0.top.equalToSuperview().inset(14) + $0.centerX.equalToSuperview() + } + } +} diff --git a/Weather_App/Weather_App/Detail/DetailHeaderView.swift b/Weather_App/Weather_App/Detail/DetailHeaderView.swift new file mode 100644 index 0000000..9e46759 --- /dev/null +++ b/Weather_App/Weather_App/Detail/DetailHeaderView.swift @@ -0,0 +1,93 @@ +// +// DetailHeaderView.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import UIKit + +import SnapKit +import Then + +class DetailHeaderView: UIView { + + // MARK: - UI Components + var detailPlaceLabel = UILabel() + var detailWeatherLabel = UILabel() + var detailCurrentTemLabel = UILabel() + var detailHighTemLabel = UILabel() + var detailLowTemLabel = UILabel() + + // MARK: - Life Cycle + override init(frame: CGRect) { + super.init(frame: frame) + + style() + hierarchy() + layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Custom Method + private func style() { + detailPlaceLabel.do{ + $0.text = "짠" + $0.font = .SFPro(.regular, size: 36) + $0.textColor = .white + } + detailWeatherLabel.do { + $0.text = "짠" + $0.font = .SFPro(.regular, size: 24) + $0.textColor = .white + } + detailCurrentTemLabel.do { + $0.text = "21" + $0.font = .SFPro(.thin, size: 102) + $0.textColor = .white + } + detailHighTemLabel.do { + $0.text = "짠" + $0.font = .SFPro(.medium, size: 20) + $0.textColor = .white + } + detailLowTemLabel.do { + $0.text = "짠" + $0.font = .SFPro(.medium, size: 20) + $0.textColor = .white + } + } + + private func hierarchy() { + self.addSubviews(detailPlaceLabel, detailWeatherLabel, detailCurrentTemLabel, detailLowTemLabel, detailHighTemLabel) + } + + private func layout() { + detailPlaceLabel.snp.makeConstraints() { + $0.top.equalToSuperview() + $0.centerX.equalToSuperview() + } + + detailCurrentTemLabel.snp.makeConstraints() { + $0.top.equalTo(detailPlaceLabel.snp.bottom).offset(4) + $0.centerX.equalToSuperview() + } + + detailWeatherLabel.snp.makeConstraints() { + $0.top.equalTo(detailCurrentTemLabel.snp.bottom).offset(4) + $0.centerX.equalToSuperview() + } + + detailHighTemLabel.snp.makeConstraints() { + $0.top.equalTo(detailWeatherLabel.snp.bottom).offset(4) + $0.trailing.equalTo(super.snp.centerX).offset(-2) + } + detailLowTemLabel.snp.makeConstraints() { + $0.top.equalTo(detailWeatherLabel.snp.bottom).offset(4) + $0.leading.equalTo(super.snp.centerX).offset(2) + } + } +} diff --git a/Weather_App/Weather_App/Detail/DetailPageViewController.swift b/Weather_App/Weather_App/Detail/DetailPageViewController.swift new file mode 100644 index 0000000..0f96a97 --- /dev/null +++ b/Weather_App/Weather_App/Detail/DetailPageViewController.swift @@ -0,0 +1,111 @@ +// +// ViewController.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +class DetailPageViewController: UIViewController { + + // MARK: - Properties + var VCIndex = 1 + var weatherDummy : [Weathers] = [] + + // MARK: - UI Components + lazy var VCList = [DetailViewController](); + var detailPageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) + var detailBottomBar = DetailBottomBar() + + // MARK: - Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + + delegate() + target() + + setHierarchy() + setLayout() + } + + // MARK: - Custom Method + + func initializePageViewController(with index: Int) { + let currentVC = VCList[index] + detailPageViewController.setViewControllers([currentVC], direction: .forward, animated: false, completion: nil) + } + + private func setHierarchy() { + self.addChild(detailPageViewController) + self.view.addSubview(detailPageViewController.view) + detailPageViewController.didMove(toParent: self) + detailPageViewController.view.addSubview(detailBottomBar) + } + + private func setLayout() { + detailPageViewController.view.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + detailBottomBar.snp.makeConstraints() { + $0.bottom.leading.trailing.equalToSuperview() + $0.height.equalTo(82) + } + } + + private func delegate() { + detailPageViewController.delegate = self + detailPageViewController.dataSource = self + + for detailVC in VCList { + detailVC.protocolDelegate = self + detailVC.weatherDummy = weatherDummy + detailVC.VCListNum = VCList.count + } + } + + private func target() { + detailBottomBar.detailListButton.addTarget(self, action: #selector(detailListBtnTap), for: .touchUpInside) + } + + @objc + private func detailListBtnTap() { + self.navigationController?.popViewController(animated: true) + } +} + +extension DetailPageViewController : UIPageViewControllerDelegate { + func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) { + guard completed else { return } + if let viewController = pageViewController.viewControllers?.first { + detailBottomBar.detailPageController.currentPage = VCList.firstIndex(of: viewController as! DetailViewController) ?? 0 + } + } +} + +extension DetailPageViewController : UIPageViewControllerDataSource { + func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { + if let currentIndex = VCList.firstIndex(of: viewController as! DetailViewController), currentIndex > 0 { + return VCList[currentIndex - 1] + } + return nil + } + + func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { + if let currentIndex = VCList.firstIndex(of: viewController as! DetailViewController), currentIndex < VCList.count - 1 { + return VCList[currentIndex + 1] + } + return nil + } +} + +extension DetailPageViewController : DetailViewControllerDelegate { + func pageScroll(_ page : Int) { + detailBottomBar.detailPageController.currentPage = page + } +} diff --git a/Weather_App/Weather_App/Detail/DetailTableViewCell.swift b/Weather_App/Weather_App/Detail/DetailTableViewCell.swift new file mode 100644 index 0000000..370948b --- /dev/null +++ b/Weather_App/Weather_App/Detail/DetailTableViewCell.swift @@ -0,0 +1,78 @@ +// +// DetailTableViewCell.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import UIKit + +import SnapKit +import Then + +class DetailTableViewCell: UITableViewCell { + + // MARK: - Properties + static let identifier: String = "DetailTableViewCell" + + // MARK: - UI Components + let detailHeaderView = DetailHeaderView() + let detailLiveView = DetailLiveView() + let detailWeekView = DetailWeekView() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setStyle() + hierarchy() + layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setStyle() { + self.do { + $0.backgroundColor = .clear + $0.contentView.isHidden = true + $0.selectedBackgroundView?.isHidden = true + } + } + + private func hierarchy() { + self.addSubviews(detailHeaderView, detailLiveView, detailWeekView) + } + + private func layout() { + detailHeaderView.snp.makeConstraints() { + $0.top.equalToSuperview().inset(34) + $0.width.equalToSuperview() + $0.height.equalTo(250) + } + + detailLiveView.snp.makeConstraints() { + $0.top.equalTo(detailHeaderView.snp.bottom).offset(44) + $0.centerX.equalToSuperview() + $0.leading.equalTo(20) + $0.height.equalTo(212) + } + + detailWeekView.snp.makeConstraints() { + $0.top.equalTo(detailLiveView.snp.bottom).offset(10) + $0.centerX.equalToSuperview() + $0.leading.equalTo(20) + $0.height.equalTo(600) + } + } +} + +extension DetailTableViewCell { + func dataBind(_ data : Weathers) { + detailHeaderView.detailPlaceLabel.text = data.name + detailHeaderView.detailWeatherLabel.text = data.weather.first?.description + detailHeaderView.detailCurrentTemLabel.text = String(Int(data.main.temp)) + "°" + detailHeaderView.detailHighTemLabel.text = "highest:" + String(Int(data.main.temp_max)) + "°" + detailHeaderView.detailLowTemLabel.text = "lowest:" + String(Int(data.main.temp_min)) + "°" + } +} diff --git a/Weather_App/Weather_App/Detail/DetailView.swift b/Weather_App/Weather_App/Detail/DetailView.swift new file mode 100644 index 0000000..1895580 --- /dev/null +++ b/Weather_App/Weather_App/Detail/DetailView.swift @@ -0,0 +1,73 @@ +// +// DetailView.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +class DetailView: UIView { + + // MARK: - Properties + + var VCListNum = 0 + var weatherData = Weathers() + + // MARK: - UI Components + + let detailBackImageView = UIImageView() + let detailTableView = UITableView() + + // MARK: - Life Cycle + override init(frame: CGRect) { + super.init(frame: frame) + + register() + + style() + hierarchy() + layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Custom Method + + private func register() { + detailTableView.register(DetailTableViewCell.self, forCellReuseIdentifier: DetailTableViewCell.identifier) + } + + private func style() { + self.backgroundColor = UIColor(hex: 0x2A3040) + + detailBackImageView.do { + $0.image = .weatherLarge + } + + detailTableView.do { + $0.backgroundColor = .clear + $0.isScrollEnabled = true + $0.showsVerticalScrollIndicator = true + } + } + + private func hierarchy() { + self.addSubviews(detailBackImageView, detailTableView) + } + + private func layout() { + detailBackImageView.snp.makeConstraints() { + $0.edges.equalToSuperview() + } + + detailTableView.snp.makeConstraints() { + $0.edges.equalToSuperview() + } + } +} diff --git a/Weather_App/Weather_App/Detail/DetailViewController.swift b/Weather_App/Weather_App/Detail/DetailViewController.swift new file mode 100644 index 0000000..3aaf5fe --- /dev/null +++ b/Weather_App/Weather_App/Detail/DetailViewController.swift @@ -0,0 +1,87 @@ +// +// DetailViewController.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +protocol DetailViewControllerDelegate : AnyObject { + func pageScroll(_ page : Int) +} + +class DetailViewController: UIViewController { + + // MARK: - Properties + var VCNum = Int() + var VCListNum = Int() + weak var protocolDelegate : DetailViewControllerDelegate? + var weatherDummy : [Weathers] = [] + + // MARK: - UI Components + private var rootView = DetailView() + + // MARK: - Life Cycle + + override func loadView() { + self.view = rootView + } + + override func viewDidLoad() { + super.viewDidLoad() + + delegate() + } + + // MARK: - Custom Method + + private func delegate() { + rootView.detailTableView.delegate = self + rootView.detailTableView.dataSource = self + } +} + +extension DetailViewController : UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: UIScreen.main.bounds.width , height: UIScreen.main.bounds.height) + } +} + +extension DetailViewController : UICollectionViewDelegate { + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + + let page = Int(targetContentOffset.pointee.x / scrollView.frame.size.width) + if(page < weatherDummy.count){ + scrollView.isScrollEnabled = true + protocolDelegate?.pageScroll(page) + } + else { + scrollView.isScrollEnabled = false + } + } +} + +extension DetailViewController: UITableViewDelegate {} +extension DetailViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 1 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: DetailTableViewCell.identifier, for: indexPath) as? DetailTableViewCell else { return DetailTableViewCell() } + cell.dataBind(weatherDummy[VCNum]) + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 1250 + } +} diff --git a/Weather_App/Weather_App/Font/UIFont+.swift b/Weather_App/Weather_App/Font/UIFont+.swift new file mode 100644 index 0000000..3e93c96 --- /dev/null +++ b/Weather_App/Weather_App/Font/UIFont+.swift @@ -0,0 +1,46 @@ +// +// File.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +//MARK: - Custom Font + +extension UIFont { + public enum SFProType: String { + case bold = "Bold" + case medium = "Medium" + case light = "Light" + case regular = "Regular" + case thin = "Thin" + } + + static func SFPro(_ type: SFProType, size: CGFloat) -> UIFont { + return UIFont(name: "SFProDisplay-\(type.rawValue)", size: size) ?? UIFont(name: "systemFont", size: 0)! + } +} + +extension UIView { + + func addSubviews(_ views: UIView...) { + for view in views { + addSubview(view) + } + } +} + +extension UIColor { + + convenience init(hex: UInt, alpha: CGFloat = 1.0) { + self.init( + red: CGFloat((hex & 0xFF0000) >> 16) / 255.0, + green: CGFloat((hex & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(hex & 0x0000FF) / 255.0, + alpha: CGFloat(alpha) + ) + } +} + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/AccentColor.colorset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/AppIcon.appiconset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconDot.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconDot.imageset/Contents.json new file mode 100644 index 0000000..a23c57f --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconDot.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 11.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconDot.imageset/Frame 11.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconDot.imageset/Frame 11.svg new file mode 100644 index 0000000..d69254f --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconDot.imageset/Frame 11.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconLightning.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconLightning.imageset/Contents.json new file mode 100644 index 0000000..e00c5f5 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconLightning.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 5.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconLightning.imageset/Frame 5.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconLightning.imageset/Frame 5.svg new file mode 100644 index 0000000..5cb0d68 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconLightning.imageset/Frame 5.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconList.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconList.imageset/Contents.json new file mode 100644 index 0000000..8b0f1c7 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconList.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 12.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconList.imageset/Frame 12.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconList.imageset/Frame 12.svg new file mode 100644 index 0000000..b0a0fa1 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconList.imageset/Frame 12.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconMap.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconMap.imageset/Contents.json new file mode 100644 index 0000000..bde26d0 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconMap.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 9.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconMap.imageset/Frame 9.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconMap.imageset/Frame 9.svg new file mode 100644 index 0000000..3adedf3 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconMap.imageset/Frame 9.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconMoon.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconMoon.imageset/Contents.json new file mode 100644 index 0000000..04464da --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconMoon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 2.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconMoon.imageset/Frame 2.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconMoon.imageset/Frame 2.svg new file mode 100644 index 0000000..8d4c7ee --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconMoon.imageset/Frame 2.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconMore.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconMore.imageset/Contents.json new file mode 100644 index 0000000..32f108d --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconMore.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 7.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconMore.imageset/Frame 7.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconMore.imageset/Frame 7.svg new file mode 100644 index 0000000..7e2ceeb --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconMore.imageset/Frame 7.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconNavigation.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconNavigation.imageset/Contents.json new file mode 100644 index 0000000..ad8b1af --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconNavigation.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 10.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconNavigation.imageset/Frame 10.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconNavigation.imageset/Frame 10.svg new file mode 100644 index 0000000..58442bf --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconNavigation.imageset/Frame 10.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconRain.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconRain.imageset/Contents.json new file mode 100644 index 0000000..76ca6a8 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconRain.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 4.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconRain.imageset/Frame 4.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconRain.imageset/Frame 4.svg new file mode 100644 index 0000000..d77743f --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconRain.imageset/Frame 4.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconRainSun.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconRainSun.imageset/Contents.json new file mode 100644 index 0000000..ae549d1 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconRainSun.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 6.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconRainSun.imageset/Frame 6.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconRainSun.imageset/Frame 6.svg new file mode 100644 index 0000000..ac0115e --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconRainSun.imageset/Frame 6.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconSearch.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconSearch.imageset/Contents.json new file mode 100644 index 0000000..d84ab34 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconSearch.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 8.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconSearch.imageset/Frame 8.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconSearch.imageset/Frame 8.svg new file mode 100644 index 0000000..b100ed1 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconSearch.imageset/Frame 8.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconSnow.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/iconSnow.imageset/Contents.json new file mode 100644 index 0000000..b40d9a4 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconSnow.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 3.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/iconSnow.imageset/Frame 3.svg b/Weather_App/Weather_App/Image/Assets.xcassets/iconSnow.imageset/Frame 3.svg new file mode 100644 index 0000000..367669f --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/iconSnow.imageset/Frame 3.svg @@ -0,0 +1,3 @@ + + + diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherDayLarge.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDayLarge.imageset/Contents.json new file mode 100644 index 0000000..11ceb7d --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDayLarge.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "스크린샷 2023-12-12 오후 11.42.59.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/Weather_App/Weather_App/Image/Assets.xcassets/weatherDayLarge.imageset/\354\212\244\355\201\254\353\246\260\354\203\267 2023-12-12 \354\230\244\355\233\204 11.42.59.png" "b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDayLarge.imageset/\354\212\244\355\201\254\353\246\260\354\203\267 2023-12-12 \354\230\244\355\233\204 11.42.59.png" new file mode 100644 index 0000000..eb473a8 Binary files /dev/null and "b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDayLarge.imageset/\354\212\244\355\201\254\353\246\260\354\203\267 2023-12-12 \354\230\244\355\233\204 11.42.59.png" differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherDaySmall.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDaySmall.imageset/Contents.json new file mode 100644 index 0000000..d4ddab5 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDaySmall.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "스크린샷 2023-12-12 오후 11.42.49.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/Weather_App/Weather_App/Image/Assets.xcassets/weatherDaySmall.imageset/\354\212\244\355\201\254\353\246\260\354\203\267 2023-12-12 \354\230\244\355\233\204 11.42.49.png" "b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDaySmall.imageset/\354\212\244\355\201\254\353\246\260\354\203\267 2023-12-12 \354\230\244\355\233\204 11.42.49.png" new file mode 100644 index 0000000..f1c3854 Binary files /dev/null and "b/Weather_App/Weather_App/Image/Assets.xcassets/weatherDaySmall.imageset/\354\212\244\355\201\254\353\246\260\354\203\267 2023-12-12 \354\230\244\355\233\204 11.42.49.png" differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Contents.json new file mode 100644 index 0000000..4bdaf9c --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Img.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Img-2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Img-3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img-2.png b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img-2.png new file mode 100644 index 0000000..4e4a2c5 Binary files /dev/null and b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img-2.png differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img-3.png b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img-3.png new file mode 100644 index 0000000..bd4eb41 Binary files /dev/null and b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img-3.png differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img.png b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img.png new file mode 100644 index 0000000..22a1f2f Binary files /dev/null and b/Weather_App/Weather_App/Image/Assets.xcassets/weatherLarge.imageset/Img.png differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/Contents.json b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/Contents.json new file mode 100644 index 0000000..5634482 --- /dev/null +++ b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "list.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "list-2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "list-3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list-2.png b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list-2.png new file mode 100644 index 0000000..12af776 Binary files /dev/null and b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list-2.png differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list-3.png b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list-3.png new file mode 100644 index 0000000..8375c7e Binary files /dev/null and b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list-3.png differ diff --git a/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list.png b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list.png new file mode 100644 index 0000000..6df80e5 Binary files /dev/null and b/Weather_App/Weather_App/Image/Assets.xcassets/weatherSmall.imageset/list.png differ diff --git a/Weather_App/Weather_App/Image/UIImage+.swift b/Weather_App/Weather_App/Image/UIImage+.swift new file mode 100644 index 0000000..8c4ce10 --- /dev/null +++ b/Weather_App/Weather_App/Image/UIImage+.swift @@ -0,0 +1,35 @@ +// +// File.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +public enum Image { + + //MARK: - Logo + + //MARK: - Icon + static let iconDot = UIImage(named: "iconDot")! + static let iconList = UIImage(named: "iconList")! + static let iconMap = UIImage(named: "iconMap")! + static let iconMore = UIImage(named: "iconMore")! + static let iconNavigation = UIImage(named: "iconNavigation")! + static let iconSearch = UIImage(named: "iconSearch")! + + //MARK: - WeatherIcon + static let iconLightning = UIImage(named: "iconLightning")! + static let iconMoon = UIImage(named: "iconMoon")! + static let iconRain = UIImage(named: "iconRain")! + static let iconRainSun = UIImage(named: "iconRainSun")! + static let iconSnow = UIImage(named: "iconSnow")! + + //MARK: - WeatherImage + static let weatherLarge = UIImage(named: "weatherLarge")! + static let weatherSmall = UIImage(named: "weatherSmall")! + static let weatherDayLarge = UIImage(named: "weatherDayLarge")! + static let weatherDaySmall = UIImage(named: "weatherDaySmall")! +} + diff --git a/Weather_App/Weather_App/Info.plist b/Weather_App/Weather_App/Info.plist new file mode 100644 index 0000000..e59ae6d --- /dev/null +++ b/Weather_App/Weather_App/Info.plist @@ -0,0 +1,31 @@ + + + + + UIAppFonts + + SF-Pro-Display-Bold.otf + SF-Pro-Display-Thin.otf + SF-Pro-Display-Medium.otf + SF-Pro-Display-Light.otf + SF-Pro-Display-Regular.otf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/Weather_App/Weather_App/List/ListTableView.swift b/Weather_App/Weather_App/List/ListTableView.swift new file mode 100644 index 0000000..283f767 --- /dev/null +++ b/Weather_App/Weather_App/List/ListTableView.swift @@ -0,0 +1,42 @@ +// +// ListTableView.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +class ListTableView: UITableView { + + // MARK: - Life Cycle + + override init(frame: CGRect, style: UITableView.Style) { + super.init(frame: .zero, style: .grouped) + + register() + tableStyle() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func register() { + self.register(ListTableViewHeader.self, forHeaderFooterViewReuseIdentifier: ListTableViewHeader.identifier) + self.register(ListTableViewCell.self, forCellReuseIdentifier: ListTableViewCell.identifier) + } + + private func tableStyle() { + self.do { + $0.backgroundColor = .clear + $0.showsVerticalScrollIndicator = false + $0.separatorStyle = .none + $0.isScrollEnabled = true + } + } +} + diff --git a/Weather_App/Weather_App/List/ListTableViewCell.swift b/Weather_App/Weather_App/List/ListTableViewCell.swift new file mode 100644 index 0000000..e1f404b --- /dev/null +++ b/Weather_App/Weather_App/List/ListTableViewCell.swift @@ -0,0 +1,202 @@ +// +// ListTableViewCell.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +protocol ListTableViewCellDelegate : AnyObject { + func listBtnTap(_ cell: UITableViewCell) +} + +class ListTableViewCell: UITableViewCell { + + // MARK: - Properties + + static let identifier: String = "ListTableViewCell" + weak var delegate: ListTableViewCellDelegate? + + // MARK: - UI Components + + var listButton = UIButton() + var placeLabel = UILabel() + var timeLabel = UILabel() + var weatherLabel = UILabel() + var currentTemLabel = UILabel() + var highTemLabel = UILabel() + var lowTemLabel = UILabel() + + let snowEmitter = CAEmitterLayer() + + // MARK: - Life Cycle + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + cellStyle() + hierarchy() + layout() + + addSnowEffect() + } + + override func layoutSubviews() { + super.layoutSubviews() + + // 레이아웃 업데이트 시 Emitter Layer의 위치와 크기를 버튼의 경계에 맞춤 + snowEmitter.frame = listButton.bounds + snowEmitter.emitterPosition = CGPoint(x: listButton.bounds.width / 2, y: 0) + snowEmitter.emitterSize = CGSize(width: listButton.bounds.width, height: 1) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + func cellStyle() { + + self.backgroundColor = .black + self.selectedBackgroundView?.isHidden = true + self.contentView.isHidden = true + + listButton.do { + $0.setBackgroundImage(Image.weatherSmall, for: .normal) + $0.addTarget(self, action: #selector(listBtnTap), for: .touchUpInside) + } + + placeLabel.do { + $0.font = .SFPro(.bold, size: 24) + $0.textColor = .white + } + + timeLabel.do { + $0.font = .SFPro(.regular, size: 17) + $0.textColor = .white + } + + weatherLabel.do { + $0.font = .SFPro(.regular, size: 16) + $0.textColor = .white + } + + currentTemLabel.do { + $0.font = .SFPro(.regular, size: 52) + $0.textColor = .white + } + highTemLabel.do { + $0.font = .SFPro(.regular, size: 15) + $0.textColor = .white + } + + lowTemLabel.do { + $0.font = .SFPro(.regular, size: 15) + $0.textColor = .white + } + } + + func hierarchy() { + self.addSubview(listButton) + listButton.addSubviews(placeLabel, timeLabel, weatherLabel, currentTemLabel, lowTemLabel, highTemLabel) + } + + func layout() { + listButton.snp.makeConstraints() { + $0.top.equalToSuperview().inset(16) + $0.bottom.equalToSuperview() + $0.centerX.equalToSuperview() + $0.leading.equalToSuperview().inset(20) + } + + placeLabel.snp.makeConstraints() { + $0.top.equalToSuperview().inset(10) + $0.leading.equalToSuperview().inset(16) + } + + timeLabel.snp.makeConstraints() { + $0.top.equalTo(placeLabel.snp.bottom).offset(2) + $0.leading.equalTo(placeLabel.snp.leading) + } + + weatherLabel.snp.makeConstraints() { + $0.bottom.equalToSuperview().inset(10) + $0.leading.equalTo(placeLabel.snp.leading) + } + + currentTemLabel.snp.makeConstraints() { + $0.top.equalToSuperview().inset(4) + $0.trailing.equalToSuperview().inset(16) + } + + lowTemLabel.snp.makeConstraints() { + $0.bottom.equalToSuperview().inset(10) + $0.trailing.equalToSuperview().inset(16) + } + + highTemLabel.snp.makeConstraints() { + $0.bottom.equalToSuperview().inset(10) + $0.trailing.equalTo(lowTemLabel.snp.leading).offset(-6) + } + } + + @objc + private func listBtnTap() { + delegate?.listBtnTap(self) + } + + private func addSnowEffect() { + // Emitter Layer의 위치와 크기를 버튼의 크기에 맞게 설정 + snowEmitter.emitterPosition = CGPoint(x: listButton.bounds.width / 2, y: 0) + snowEmitter.emitterShape = .line + snowEmitter.emitterSize = CGSize(width: listButton.bounds.width, height: 1) + snowEmitter.frame = listButton.bounds + + let snowflake = CAEmitterCell() + snowflake.contents = UIImage.iconNavigation.cgImage + snowflake.birthRate = 20 + snowflake.lifetime = Float(listButton.bounds.height / 50) // 버튼 높이에 맞춰 조정 + snowflake.velocity = 50 // 수직 속도, 필요에 따라 조정 + snowflake.velocityRange = 20 + snowflake.yAcceleration = 100 + snowflake.scale = 0.05 + snowflake.scaleRange = 0.02 + + snowEmitter.emitterCells = [snowflake] + listButton.layer.addSublayer(snowEmitter) + } +} + +extension ListTableViewCell { + func dataBind(_ weather: Weathers){ + placeLabel.text = String(weather.name) + timeLabel.text = convertTime(timezone: weather.timezone) + weatherLabel.text = weather.weather.first?.description + currentTemLabel.text = String(Int(weather.main.temp)) + "°" + highTemLabel.text = "highest:" + String(Int(weather.main.temp_max)) + "°" + lowTemLabel.text = "lowest:" + String(Int(weather.main.temp_min)) + "°" + } + + func convertTime(timezone: Int) -> String { + guard let timeZone = TimeZone(secondsFromGMT: timezone) else { + return "Invalid Timezone" + } + + let dateFormatter = DateFormatter() + + //24시간 기준 => HH + //12시간 기준 => hh + //AM, PM 표기 => a 위치 + + dateFormatter.dateFormat = "a hh:mm" + dateFormatter.amSymbol = "AM" + dateFormatter.pmSymbol = "PM" + dateFormatter.timeZone = timeZone + let localTime = dateFormatter.string(from: Date()) + return localTime + } +} diff --git a/Weather_App/Weather_App/List/ListTableViewHeader.swift b/Weather_App/Weather_App/List/ListTableViewHeader.swift new file mode 100644 index 0000000..209de6e --- /dev/null +++ b/Weather_App/Weather_App/List/ListTableViewHeader.swift @@ -0,0 +1,75 @@ +// +// ListTableViewHeader.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +class ListTableViewHeader: UITableViewHeaderFooterView { + + // MARK: - Properties + + static let identifier: String = "ListTableViewHeader" + + // MARK: - UI Components + + var listWeatherLabel = UILabel() + var listWeatherSearchBar = UISearchBar() + + // MARK: - Life Cycle + + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + + style() + hierarchy() + layout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Custom Method + + private func style() { + listWeatherLabel.do { + $0.text = "날씨" + $0.font = .SFPro(.bold, size: 36) + $0.textColor = .white + } + + listWeatherSearchBar.do { + $0.backgroundColor = .white + $0.placeholder = "도시 또는 공항 검색" + $0.barStyle = .black + $0.clipsToBounds = true + $0.setShowsCancelButton(false, animated: true) + $0.barTintColor = .clear + $0.setImage(UIImage(), for: UISearchBar.Icon.clear, state: .normal) + } + } + + private func hierarchy() { + self.addSubviews(listWeatherLabel, listWeatherSearchBar) + } + + private func layout() { + listWeatherLabel.snp.makeConstraints() { + $0.top.equalToSuperview() + $0.leading.equalToSuperview().inset(20) + } + + listWeatherSearchBar.snp.makeConstraints() { + $0.top.equalTo(listWeatherLabel.snp.bottom).offset(8) + $0.leading.trailing.equalToSuperview().inset(10) + $0.height.equalTo(40) + } + } +} + diff --git a/Weather_App/Weather_App/List/ListView.swift b/Weather_App/Weather_App/List/ListView.swift new file mode 100644 index 0000000..68bd16f --- /dev/null +++ b/Weather_App/Weather_App/List/ListView.swift @@ -0,0 +1,51 @@ +// +// ListView.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +class ListView: UIView { + + // MARK: - UI Components + + var listTableView = ListTableView() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: frame) + + style() + hierarchy() + layout() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Custom Method + + private func style() { + self.do { + $0.backgroundColor = .black + } + } + + private func hierarchy() { + self.addSubview(listTableView) + } + + private func layout() { + listTableView.snp.makeConstraints() { + $0.edges.equalToSuperview() + } + } +} diff --git a/Weather_App/Weather_App/List/ListViewController.swift b/Weather_App/Weather_App/List/ListViewController.swift new file mode 100644 index 0000000..e7d4f79 --- /dev/null +++ b/Weather_App/Weather_App/List/ListViewController.swift @@ -0,0 +1,166 @@ +// +// ViewController.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +import SnapKit +import Then + +class ListViewController: UIViewController { + + // MARK: - Properties + private var weatherDummy: [Weathers] = [] + private var filteredWeatherData: [Weathers] = [] + private var isFiltering: Bool = false + private var isSearching: Bool = false + + // MARK: - UI Components + + private let rootView = ListView() + + // MARK: - Life Cycle + + override func loadView() { + self.view = rootView + } + + override func viewDidLoad() { + super.viewDidLoad() + + getWeathers(cities: Cities) + + setNavi() + delegate() + } + + // MARK: - Custom Method + private func setNavi() { + let backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil) + backBarButtonItem.tintColor = .black + self.navigationItem.backBarButtonItem = backBarButtonItem + } + + private func delegate() { + rootView.listTableView.delegate = self + rootView.listTableView.dataSource = self + } + +} + +extension ListViewController : UITableViewDelegate { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 117.0 + 16.0 + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: ListTableViewHeader.identifier) as? ListTableViewHeader else { return UIView()} + header.listWeatherSearchBar.delegate = self + return header + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return 88.0 + } +} + +extension ListViewController : UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return isFiltering ? filteredWeatherData.count : weatherDummy.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ListTableViewCell.identifier, for: indexPath) as? ListTableViewCell else { return UITableViewCell() } + cell.delegate = self + let weatherData = isFiltering ? filteredWeatherData[indexPath.row] : weatherDummy[indexPath.row] + cell.dataBind(weatherData) + return cell + } +} + +extension ListViewController: ListTableViewCellDelegate { + func listBtnTap(_ cell: UITableViewCell) { + guard weatherDummy.count == Cities.count else { + print("데이터가 아직 로드되지 않았습니다.") + return + } + + if let indexPath = rootView.listTableView.indexPath(for: cell) { + let detailPageVC = DetailPageViewController() + for index in 0.. detailWeekDataMax){ + detailWeekDataMax = low + } + + if(high > detailWeekDataMax){ + detailWeekDataMax = high + } + } +} diff --git a/Weather_App/Weather_App/Network/DetailEnum.swift b/Weather_App/Weather_App/Network/DetailEnum.swift new file mode 100644 index 0000000..f620027 --- /dev/null +++ b/Weather_App/Weather_App/Network/DetailEnum.swift @@ -0,0 +1,39 @@ +// +// DetailEnum.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import UIKit + +struct Detail { + let time : String + let icon : Icon + let tem : String +} + +@frozen +enum Icon { + case lightning + case moon + case rain + case rainsun + case snow + + var image : UIImage { + switch self { + case .lightning: + return Image.iconLightning + case .moon: + return Image.iconMoon + case .rain: + return Image.iconRain + case .rainsun: + return Image.iconRainSun + case .snow: + return Image.iconSnow + } + } +} + diff --git a/Weather_App/Weather_App/Network/DetailEnum2.swift b/Weather_App/Weather_App/Network/DetailEnum2.swift new file mode 100644 index 0000000..da13064 --- /dev/null +++ b/Weather_App/Weather_App/Network/DetailEnum2.swift @@ -0,0 +1,39 @@ +// +// DetailEnum2.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import UIKit + +struct Detail2 { + let day : String + let icon : Icon2 + let lowTem : Int + let highTem : Int +} + +@frozen +enum Icon2 { + case lightning + case moon + case rain + case rainsun + case snow + + var image : UIImage { + switch self { + case .lightning: + return Image.iconLightning + case .moon: + return Image.iconMoon + case .rain: + return Image.iconRain + case .rainsun: + return Image.iconRainSun + case .snow: + return Image.iconSnow + } + } +} diff --git a/Weather_App/Weather_App/Network/GetWeather.swift b/Weather_App/Weather_App/Network/GetWeather.swift new file mode 100644 index 0000000..7ca3204 --- /dev/null +++ b/Weather_App/Weather_App/Network/GetWeather.swift @@ -0,0 +1,28 @@ +// +// GetWeather.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import UIKit + +class GetWeather { + func getWeathers(cities : [String], view : UIView) { + for i in cities { + WeatherService.shared.getWeather(forCity: i) { weather, error in + if let error = error { + print("Error: \(error)") + } else if let weather = weather { + DispatchQueue.main.async { // 메인 스레드로 전환 + self.weatherDummy.append(weather) + self.loadData() + } + } + } + } + } + + func loadData() { + rootView.listTableView.reloadData() +} diff --git a/Weather_App/Weather_App/Network/WeatherBody.swift b/Weather_App/Weather_App/Network/WeatherBody.swift new file mode 100644 index 0000000..c15c461 --- /dev/null +++ b/Weather_App/Weather_App/Network/WeatherBody.swift @@ -0,0 +1,72 @@ +// +// WeatherBody.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import Foundation + +struct Weathers: Codable { + let weather: [Weather] + let main: Main + let wind: Wind + let name: String + let timezone : Int + + init(weather: [Weather] = [Weather()], + main: Main = Main(), + wind: Wind = Wind(), + name: String = "", + timezone: Int = 0) { + self.weather = weather + self.main = main + self.wind = wind + self.name = name + self.timezone = timezone + } +} + +struct Weather: Codable { + let id: Int + let main: String + let description: String + let icon: String + + init(id: Int = 0, + main: String = "", + description: String = "", + icon: String = "") { + self.id = id + self.main = main + self.description = description + self.icon = icon + } +} + +struct Main: Codable { + let temp: Double + let temp_min: Double + let temp_max: Double + let humidity: Int + + init(temp: Double = 0.0, + temp_min: Double = 0.0, + temp_max: Double = 0.0, + humidity: Int = 0) { + self.temp = temp + self.temp_min = temp_min + self.temp_max = temp_max + self.humidity = humidity + } +} + +struct Wind: Codable { + let speed: Double + + init(speed: Double = 0.0) { + self.speed = speed + } +} + + diff --git a/Weather_App/Weather_App/Network/WeatherService.swift b/Weather_App/Weather_App/Network/WeatherService.swift new file mode 100644 index 0000000..e82b8b6 --- /dev/null +++ b/Weather_App/Weather_App/Network/WeatherService.swift @@ -0,0 +1,46 @@ +// +// WeatherService.swift +// Weather_App +// +// Created by 신지원 on 12/12/23. +// + +import Foundation + +class WeatherService { + + //보지마💛 + let apiKey = "9c7fa18b50b00954d1159672fb66a924" + static let shared = WeatherService() + + func getWeather(forCity city: String, completion: @escaping (Weathers?, Error?) -> Void) { + + let apiUrl = "https://api.openweathermap.org/data/2.5/weather?q=\(city)&appid=\(apiKey)&units=metric" + guard let url = URL(string: apiUrl) else { + completion(nil, nil) + return + } + + let task = URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + completion(nil, error) + return + } + + guard let data = data else { + completion(nil, nil) + return + } + + do { + let decodedData = try JSONDecoder().decode(Weathers.self, from: data) + completion(decodedData, nil) + } catch { + completion(nil, error) + } + } + + task.resume() + } +} + diff --git a/Weather_App/Weather_App/SceneDelegate.swift b/Weather_App/Weather_App/SceneDelegate.swift new file mode 100644 index 0000000..2e7b3a6 --- /dev/null +++ b/Weather_App/Weather_App/SceneDelegate.swift @@ -0,0 +1,51 @@ +// +// SceneDelegate.swift +// Weather_App +// +// Created by 신지원 on 12/11/23. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + guard let windowScene = (scene as? UIWindowScene) else { return } + let window = UIWindow(windowScene: windowScene) + let navigationController = UINavigationController(rootViewController: ListViewController()) + window.rootViewController = navigationController + window.makeKeyAndVisible() + self.window = window + } + + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } +}