diff --git a/SOPTWeather/SOPTWeather.xcodeproj/project.pbxproj b/SOPTWeather/SOPTWeather.xcodeproj/project.pbxproj index a6d83c2..d44b065 100644 --- a/SOPTWeather/SOPTWeather.xcodeproj/project.pbxproj +++ b/SOPTWeather/SOPTWeather.xcodeproj/project.pbxproj @@ -7,20 +7,22 @@ objects = { /* Begin PBXBuildFile section */ - 366014902AFB70B200D3D4F7 /* CityWeatherDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3660148F2AFB70B200D3D4F7 /* CityWeatherDetailView.swift */; }; + 3651D8BD2B20639600BE0699 /* LocationListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3651D8BC2B20639600BE0699 /* LocationListViewModel.swift */; }; + 3651D8BF2B21074F00BE0699 /* Observable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3651D8BE2B21074F00BE0699 /* Observable.swift */; }; 366014932AFB73F300D3D4F7 /* TimeWeatherCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366014922AFB73F300D3D4F7 /* TimeWeatherCollectionViewCell.swift */; }; - 366014952AFB740000D3D4F7 /* WeeklyWeatherCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366014942AFB740000D3D4F7 /* WeeklyWeatherCollectionViewCell.swift */; }; 366014972AFB7DF700D3D4F7 /* CollectionViewCellReuseProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366014962AFB7DF700D3D4F7 /* CollectionViewCellReuseProtocol.swift */; }; - 366014992AFE0E0500D3D4F7 /* SummaryCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366014982AFE0E0500D3D4F7 /* SummaryCollectionViewCell.swift */; }; 3660149D2AFE1E9B00D3D4F7 /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3660149C2AFE1E9B00D3D4F7 /* UIImage+.swift */; }; - 3660149F2AFE21A300D3D4F7 /* SectionBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3660149E2AFE21A300D3D4F7 /* SectionBackgroundView.swift */; }; - 366014A12AFE2CA900D3D4F7 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366014A02AFE2CA900D3D4F7 /* HeaderView.swift */; }; 366646612B03715B006C8B5E /* WeatherModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366646602B03715B006C8B5E /* WeatherModel.swift */; }; 366646632B03898B006C8B5E /* GetWeatherService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366646622B03898B006C8B5E /* GetWeatherService.swift */; }; 366646652B04A52F006C8B5E /* GetTimeWeatherInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366646642B04A52F006C8B5E /* GetTimeWeatherInfo.swift */; }; 3697EE9E2AEB249200115D49 /* WeatherDetailPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3697EE9D2AEB249200115D49 /* WeatherDetailPageViewController.swift */; }; 36B2B80D2AEA4D7400755C63 /* TimeWeatherModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B2B80C2AEA4D7400755C63 /* TimeWeatherModel.swift */; }; - 36B2B80F2AEA4E7200755C63 /* TimeWeatherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36B2B80E2AEA4E7200755C63 /* TimeWeatherView.swift */; }; + 36CC2C3A2B2742360066B942 /* CityWeatherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC2C392B2742360066B942 /* CityWeatherView.swift */; }; + 36CC2C3E2B274EA80066B942 /* WeatherTimeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC2C3D2B274EA80066B942 /* WeatherTimeHeaderView.swift */; }; + 36CC2C412B2761F00066B942 /* TimeWeatherTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC2C402B2761F00066B942 /* TimeWeatherTableViewCell.swift */; }; + 36CC2C432B276E370066B942 /* WeekWeatherTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC2C422B276E370066B942 /* WeekWeatherTableViewCell.swift */; }; + 36CC2C452B2770730066B942 /* WeekHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC2C442B2770730066B942 /* WeekHeaderView.swift */; }; + 36CC2C472B2770BC0066B942 /* WeatherDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36CC2C462B2770BC0066B942 /* WeatherDetailViewModel.swift */; }; 36E60E412AE180FA005035D3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E402AE180FA005035D3 /* AppDelegate.swift */; }; 36E60E432AE180FA005035D3 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E422AE180FA005035D3 /* SceneDelegate.swift */; }; 36E60E4A2AE180FB005035D3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 36E60E492AE180FB005035D3 /* Assets.xcassets */; }; @@ -38,8 +40,7 @@ 36E60E802AE2DF6A005035D3 /* LocationListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E7F2AE2DF6A005035D3 /* LocationListView.swift */; }; 36E60E832AE2E0AB005035D3 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 36E60E822AE2E0AB005035D3 /* SnapKit */; }; 36E60E852AE2E768005035D3 /* LocationListElementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E842AE2E768005035D3 /* LocationListElementView.swift */; }; - 36E60E8B2AE2F0AC005035D3 /* WeatherDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E8A2AE2F0AC005035D3 /* WeatherDetailView.swift */; }; - 36E60E8D2AE2F0C6005035D3 /* WeatherDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E8C2AE2F0C6005035D3 /* WeatherDetailViewController.swift */; }; + 36E60E8D2AE2F0C6005035D3 /* CityWeatherDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E60E8C2AE2F0C6005035D3 /* CityWeatherDetailViewController.swift */; }; 36E994BF2AFAAE6C00F1B2C9 /* WeatherListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36E994BE2AFAAE6C00F1B2C9 /* WeatherListTableViewCell.swift */; }; 36EC085C2B02243F008A820D /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36EC085B2B02243F008A820D /* Config.swift */; }; 36EC085E2B0228D9008A820D /* NetworkResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36EC085D2B0228D9008A820D /* NetworkResult.swift */; }; @@ -48,20 +49,22 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 3660148F2AFB70B200D3D4F7 /* CityWeatherDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityWeatherDetailView.swift; sourceTree = ""; }; + 3651D8BC2B20639600BE0699 /* LocationListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationListViewModel.swift; sourceTree = ""; }; + 3651D8BE2B21074F00BE0699 /* Observable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Observable.swift; sourceTree = ""; }; 366014922AFB73F300D3D4F7 /* TimeWeatherCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeWeatherCollectionViewCell.swift; sourceTree = ""; }; - 366014942AFB740000D3D4F7 /* WeeklyWeatherCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeeklyWeatherCollectionViewCell.swift; sourceTree = ""; }; 366014962AFB7DF700D3D4F7 /* CollectionViewCellReuseProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewCellReuseProtocol.swift; sourceTree = ""; }; - 366014982AFE0E0500D3D4F7 /* SummaryCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummaryCollectionViewCell.swift; sourceTree = ""; }; 3660149C2AFE1E9B00D3D4F7 /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; }; - 3660149E2AFE21A300D3D4F7 /* SectionBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionBackgroundView.swift; sourceTree = ""; }; - 366014A02AFE2CA900D3D4F7 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; 366646602B03715B006C8B5E /* WeatherModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherModel.swift; sourceTree = ""; }; 366646622B03898B006C8B5E /* GetWeatherService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetWeatherService.swift; sourceTree = ""; }; 366646642B04A52F006C8B5E /* GetTimeWeatherInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetTimeWeatherInfo.swift; sourceTree = ""; }; 3697EE9D2AEB249200115D49 /* WeatherDetailPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDetailPageViewController.swift; sourceTree = ""; }; 36B2B80C2AEA4D7400755C63 /* TimeWeatherModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeWeatherModel.swift; sourceTree = ""; }; - 36B2B80E2AEA4E7200755C63 /* TimeWeatherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeWeatherView.swift; sourceTree = ""; }; + 36CC2C392B2742360066B942 /* CityWeatherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityWeatherView.swift; sourceTree = ""; }; + 36CC2C3D2B274EA80066B942 /* WeatherTimeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherTimeHeaderView.swift; sourceTree = ""; }; + 36CC2C402B2761F00066B942 /* TimeWeatherTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeWeatherTableViewCell.swift; sourceTree = ""; }; + 36CC2C422B276E370066B942 /* WeekWeatherTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekWeatherTableViewCell.swift; sourceTree = ""; }; + 36CC2C442B2770730066B942 /* WeekHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekHeaderView.swift; sourceTree = ""; }; + 36CC2C462B2770BC0066B942 /* WeatherDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDetailViewModel.swift; sourceTree = ""; }; 36E60E3D2AE180FA005035D3 /* SOPTWeather.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SOPTWeather.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36E60E402AE180FA005035D3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36E60E422AE180FA005035D3 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -80,8 +83,7 @@ 36E60E7D2AE2DF59005035D3 /* LocationListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationListViewController.swift; sourceTree = ""; }; 36E60E7F2AE2DF6A005035D3 /* LocationListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationListView.swift; sourceTree = ""; }; 36E60E842AE2E768005035D3 /* LocationListElementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationListElementView.swift; sourceTree = ""; }; - 36E60E8A2AE2F0AC005035D3 /* WeatherDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDetailView.swift; sourceTree = ""; }; - 36E60E8C2AE2F0C6005035D3 /* WeatherDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherDetailViewController.swift; sourceTree = ""; }; + 36E60E8C2AE2F0C6005035D3 /* CityWeatherDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CityWeatherDetailViewController.swift; sourceTree = ""; }; 36E994BE2AFAAE6C00F1B2C9 /* WeatherListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherListTableViewCell.swift; sourceTree = ""; }; 36EC085B2B02243F008A820D /* Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; 36EC085D2B0228D9008A820D /* NetworkResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkResult.swift; sourceTree = ""; }; @@ -102,14 +104,21 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3651D8BB2B20638800BE0699 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 3651D8BC2B20639600BE0699 /* LocationListViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; 366014912AFB739B00D3D4F7 /* Cells */ = { isa = PBXGroup; children = ( - 366014982AFE0E0500D3D4F7 /* SummaryCollectionViewCell.swift */, + 36CC2C3F2B274EAE0066B942 /* Helper */, 366014922AFB73F300D3D4F7 /* TimeWeatherCollectionViewCell.swift */, - 366014942AFB740000D3D4F7 /* WeeklyWeatherCollectionViewCell.swift */, - 3660149E2AFE21A300D3D4F7 /* SectionBackgroundView.swift */, - 366014A02AFE2CA900D3D4F7 /* HeaderView.swift */, + 36CC2C402B2761F00066B942 /* TimeWeatherTableViewCell.swift */, + 36CC2C422B276E370066B942 /* WeekWeatherTableViewCell.swift */, ); path = Cells; sourceTree = ""; @@ -118,7 +127,7 @@ isa = PBXGroup; children = ( 3697EE9D2AEB249200115D49 /* WeatherDetailPageViewController.swift */, - 36E60E8C2AE2F0C6005035D3 /* WeatherDetailViewController.swift */, + 36E60E8C2AE2F0C6005035D3 /* CityWeatherDetailViewController.swift */, ); path = ViewControllers; sourceTree = ""; @@ -126,13 +135,28 @@ 3660149B2AFE0E1500D3D4F7 /* Views */ = { isa = PBXGroup; children = ( - 3660148F2AFB70B200D3D4F7 /* CityWeatherDetailView.swift */, - 36E60E8A2AE2F0AC005035D3 /* WeatherDetailView.swift */, - 36B2B80E2AEA4E7200755C63 /* TimeWeatherView.swift */, + 36CC2C392B2742360066B942 /* CityWeatherView.swift */, ); path = Views; sourceTree = ""; }; + 36CC2C3F2B274EAE0066B942 /* Helper */ = { + isa = PBXGroup; + children = ( + 36CC2C3D2B274EA80066B942 /* WeatherTimeHeaderView.swift */, + 36CC2C442B2770730066B942 /* WeekHeaderView.swift */, + ); + path = Helper; + sourceTree = ""; + }; + 36CC2C4B2B2C973D0066B942 /* ViewModel */ = { + isa = PBXGroup; + children = ( + 36CC2C462B2770BC0066B942 /* WeatherDetailViewModel.swift */, + ); + path = ViewModel; + sourceTree = ""; + }; 36E60E342AE180F9005035D3 = { isa = PBXGroup; children = ( @@ -253,6 +277,7 @@ isa = PBXGroup; children = ( 366014962AFB7DF700D3D4F7 /* CollectionViewCellReuseProtocol.swift */, + 3651D8BE2B21074F00BE0699 /* Observable.swift */, ); path = Protocols; sourceTree = ""; @@ -260,6 +285,7 @@ 36E60E862AE2EC6B005035D3 /* LocationList */ = { isa = PBXGroup; children = ( + 3651D8BB2B20638800BE0699 /* ViewModel */, 36E994BD2AFAAE4100F1B2C9 /* Cell */, 36E60E882AE2ED11005035D3 /* ViewController */, 36E60E892AE2ED1C005035D3 /* View */, @@ -270,6 +296,7 @@ 36E60E872AE2EC7D005035D3 /* WeatherDetail */ = { isa = PBXGroup; children = ( + 36CC2C4B2B2C973D0066B942 /* ViewModel */, 3660149A2AFE0E0D00D3D4F7 /* ViewControllers */, 3660149B2AFE0E1500D3D4F7 /* Views */, 366014912AFB739B00D3D4F7 /* Cells */, @@ -412,27 +439,28 @@ 36E60E7A2AE26566005035D3 /* UIView+.swift in Sources */, 36EC085C2B02243F008A820D /* Config.swift in Sources */, 36E60E732AE2595D005035D3 /* Font.swift in Sources */, - 36E60E8B2AE2F0AC005035D3 /* WeatherDetailView.swift in Sources */, - 3660149F2AFE21A300D3D4F7 /* SectionBackgroundView.swift in Sources */, - 366014952AFB740000D3D4F7 /* WeeklyWeatherCollectionViewCell.swift in Sources */, + 36CC2C3A2B2742360066B942 /* CityWeatherView.swift in Sources */, + 3651D8BD2B20639600BE0699 /* LocationListViewModel.swift in Sources */, 366646612B03715B006C8B5E /* WeatherModel.swift in Sources */, 36E60E772AE25975005035D3 /* ImageLiterals.swift in Sources */, - 36E60E8D2AE2F0C6005035D3 /* WeatherDetailViewController.swift in Sources */, + 36E60E8D2AE2F0C6005035D3 /* CityWeatherDetailViewController.swift in Sources */, 36EC08622B023348008A820D /* GetService.swift in Sources */, 366014932AFB73F300D3D4F7 /* TimeWeatherCollectionViewCell.swift in Sources */, - 366014992AFE0E0500D3D4F7 /* SummaryCollectionViewCell.swift in Sources */, - 36B2B80F2AEA4E7200755C63 /* TimeWeatherView.swift in Sources */, + 3651D8BF2B21074F00BE0699 /* Observable.swift in Sources */, 36E60E412AE180FA005035D3 /* AppDelegate.swift in Sources */, 36E60E7E2AE2DF59005035D3 /* LocationListViewController.swift in Sources */, 3660149D2AFE1E9B00D3D4F7 /* UIImage+.swift in Sources */, + 36CC2C3E2B274EA80066B942 /* WeatherTimeHeaderView.swift in Sources */, 3697EE9E2AEB249200115D49 /* WeatherDetailPageViewController.swift in Sources */, 36E60E852AE2E768005035D3 /* LocationListElementView.swift in Sources */, - 366014902AFB70B200D3D4F7 /* CityWeatherDetailView.swift in Sources */, + 36CC2C452B2770730066B942 /* WeekHeaderView.swift in Sources */, + 36CC2C412B2761F00066B942 /* TimeWeatherTableViewCell.swift in Sources */, + 36CC2C432B276E370066B942 /* WeekWeatherTableViewCell.swift in Sources */, + 36CC2C472B2770BC0066B942 /* WeatherDetailViewModel.swift in Sources */, 36EC085E2B0228D9008A820D /* NetworkResult.swift in Sources */, 366646632B03898B006C8B5E /* GetWeatherService.swift in Sources */, 36EC08602B0228F9008A820D /* NetworkError.swift in Sources */, 36E60E432AE180FA005035D3 /* SceneDelegate.swift in Sources */, - 366014A12AFE2CA900D3D4F7 /* HeaderView.swift in Sources */, 366646652B04A52F006C8B5E /* GetTimeWeatherInfo.swift in Sources */, 36E994BF2AFAAE6C00F1B2C9 /* WeatherListTableViewCell.swift in Sources */, ); diff --git a/SOPTWeather/SOPTWeather/Application/SceneDelegate.swift b/SOPTWeather/SOPTWeather/Application/SceneDelegate.swift index c400637..c01bcfe 100644 --- a/SOPTWeather/SOPTWeather/Application/SceneDelegate.swift +++ b/SOPTWeather/SOPTWeather/Application/SceneDelegate.swift @@ -15,7 +15,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } self.window = UIWindow(windowScene: windowScene) - let navigationController = UINavigationController(rootViewController: LocationListViewController()) + let navigationController = UINavigationController(rootViewController: CityWeatherDetailViewController()) self.window?.rootViewController = navigationController self.window?.makeKeyAndVisible() diff --git a/SOPTWeather/SOPTWeather/Global/Protocols/Observable.swift b/SOPTWeather/SOPTWeather/Global/Protocols/Observable.swift new file mode 100644 index 0000000..4ea8d2a --- /dev/null +++ b/SOPTWeather/SOPTWeather/Global/Protocols/Observable.swift @@ -0,0 +1,27 @@ +// +// Observable.swift +// SOPTWeather +// +// Created by 지희의 MAC on 12/7/23. +// + +import Foundation + +class Observable { + private var listener: ((T) -> Void)? + + var value: T { + didSet { + listener?(value) + } + } + + init(_ value: T){ + self.value = value + } + + func bind(_ closure: @escaping (T) -> Void ) { + closure(value) + listener = closure + } +} diff --git a/SOPTWeather/SOPTWeather/Presentation/LocationList/Cell/WeatherListTableViewCell.swift b/SOPTWeather/SOPTWeather/Presentation/LocationList/Cell/WeatherListTableViewCell.swift index 22970dc..1ef8813 100644 --- a/SOPTWeather/SOPTWeather/Presentation/LocationList/Cell/WeatherListTableViewCell.swift +++ b/SOPTWeather/SOPTWeather/Presentation/LocationList/Cell/WeatherListTableViewCell.swift @@ -11,11 +11,7 @@ class WeatherListTableViewCell: UITableViewCell { static let cellReuseIdentifier = "cellReuseIdentifier" - var weatherData = (cityName: "", weatherText: "", maxminTemp: "", weatherinfomation: []){ - didSet{ - bindData() - } - } + var weatherData = (cityName: "", weatherText: "", maxminTemp: "", weatherinfomation: []) private let backgroundImageView: UIImageView = { let imageView = UIImageView() @@ -75,21 +71,6 @@ class WeatherListTableViewCell: UITableViewCell { contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)) } - func bindData() { - locationTitleLabel.text = weatherData.cityName - locationLabel.text = weatherData.subTitle - weatherLabel.text = weatherData.weatherText - - if weatherData.currentTemp == "" { - tempLabel.text = weatherData.weatherinfomation.first?.tempText - } else { - tempLabel.text = weatherData.currentTemp - } - - maxMinTempLabel.text = weatherData.maxminTemp - setUI() - } - private func setUI(){ setViewHierarchy() setConstraints() diff --git a/SOPTWeather/SOPTWeather/Presentation/LocationList/View/LocationListElementView.swift b/SOPTWeather/SOPTWeather/Presentation/LocationList/View/LocationListElementView.swift index 21e8146..86451b4 100644 --- a/SOPTWeather/SOPTWeather/Presentation/LocationList/View/LocationListElementView.swift +++ b/SOPTWeather/SOPTWeather/Presentation/LocationList/View/LocationListElementView.swift @@ -70,9 +70,9 @@ class LocationListElementView: UIButton { func bindData() { locationTitleLabel.text = weatherData.cityName - locationLabel.text = weatherData.subTitle + // locationLabel.text = weatherData.subTitle weatherLabel.text = weatherData.weatherText - tempLabel.text = weatherData.weatherinfomation.first?.tempText + // tempLabel.text = weatherData.weatherinfomation.first?.tempText maxMinTempLabel.text = weatherData.maxminTemp setUI() } diff --git a/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewController/LocationListViewController.swift b/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewController/LocationListViewController.swift index 8766a22..3072173 100644 --- a/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewController/LocationListViewController.swift +++ b/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewController/LocationListViewController.swift @@ -10,54 +10,23 @@ import UIKit class LocationListViewController: UIViewController { let pageController = WeatherDetailPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal) - let locationView = LocationListView() - let locationList = ["iksan", "jeonju", "jeju", "cheonan", "cheongju", "chuncheon"] - var infoList: [] = [] + let locationListView = LocationListView() - override func viewDidLoad() { - super.viewDidLoad() - navigationController?.isNavigationBarHidden = true - getWeatherInfo() - bindVC() - setDelegate() - addView() - } - private func addView() { - view.addSubview(locationView) - locationView.snp.makeConstraints { - $0.edges.equalTo(view.safeAreaLayoutGuide) - } + override func loadView() { + self.view = locationListView } - private func setDelegate() { - locationView.searchBar.delegate = self - locationView.weatherTableView.dataSource = self - locationView.weatherTableView.delegate = self - } - private func bindVC(){ - - for i in 0.. Int { - return infoList.count + return 4 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: WeatherListTableViewCell.cellReuseIdentifier, for: indexPath) as! WeatherListTableViewCell - cell.weatherData = infoList[indexPath.row] + // cell.weatherData = infoList[indexPath.row] cell.selectionStyle = .none return cell } diff --git a/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewModel/LocationListViewModel.swift b/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewModel/LocationListViewModel.swift new file mode 100644 index 0000000..bb6a2b3 --- /dev/null +++ b/SOPTWeather/SOPTWeather/Presentation/LocationList/ViewModel/LocationListViewModel.swift @@ -0,0 +1,58 @@ +// +// LocationListViewModel.swift +// SOPTWeather +// +// Created by 지희의 MAC on 12/6/23. +// + +import Foundation +import UIKit + +protocol ObservableViewModelProtocol { + func fetchWeatherData() + func setError(_ message: String) + var weather: Observable<[WeatherDTO]> { get set } + var errorMessage: Observable { get set } + var error: Observable { get set } + } + +class LocationListViewModel: NSObject, ObservableViewModelProtocol { + + let locationList = ["iksan", "jeonju", "jeju", "cheonan", "cheongju", "chuncheon"] + + var weather: Observable<[WeatherDTO]> = Observable([]) + var errorMessage: Observable = Observable(nil) + var error: Observable = Observable(false) + + func fetchWeatherData() { + locationList.forEach { city in + Task { + if let result = try await GetWeatherService.shared.GetWeatherInfo(location: city) { + print(result) + weather.value.append(result) + } + } + } + + } + + func setError(_ message: String) { + self.errorMessage = Observable(message) + self.error = Observable(true) + } +} + + +// 근데 여기서 만약 아직 네트워크가 실행되지 않았다면 ? 테이블 뷰가 나타나지 않을거임.. 고민해보자 +extension LocationListViewModel: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return weather.value.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: WeatherListTableViewCell.cellReuseIdentifier, for: indexPath) as! WeatherListTableViewCell + // cell.weatherData = infoList[indexPath.row] + cell.selectionStyle = .none + return cell + } +} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/HeaderView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/HeaderView.swift deleted file mode 100644 index 007b21a..0000000 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/HeaderView.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// HeaderView.swift -// SOPTWeather -// -// Created by 지희의 MAC on 2023/11/10. -// - -import UIKit - -class HeaderView: UICollectionReusableView { - static var reuseId = "HeaderView" - - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/SummaryCollectionViewCell.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/Helper/WeatherTimeHeaderView.swift similarity index 71% rename from SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/SummaryCollectionViewCell.swift rename to SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/Helper/WeatherTimeHeaderView.swift index bcfa050..e9a29d4 100644 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/SummaryCollectionViewCell.swift +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/Helper/WeatherTimeHeaderView.swift @@ -1,18 +1,25 @@ // -// SummaryCollectionViewCell.swift +// HeaderView.swift // SOPTWeather // -// Created by 지희의 MAC on 2023/11/10. +// Created by 지희의 MAC on 12/11/23. // import UIKit -class SummaryCollectionViewCell: UICollectionViewCell { +import SnapKit + +final class WeatherTimeHeaderView: UITableViewHeaderFooterView { + // MARK: - Variables + // MARK: Const + static var reuseId = "WeatherTimeHeaderView" + // MARK: Component private let cityTitleLabel: UILabel = { let label = UILabel() label.font = .sfProRegular36 label.textColor = .white + label.text = "고양시" return label }() @@ -20,6 +27,7 @@ class SummaryCollectionViewCell: UICollectionViewCell { let label = UILabel() label.font = .sfProThin102 label.textColor = .white + label.text = "0℃" return label }() @@ -27,6 +35,7 @@ class SummaryCollectionViewCell: UICollectionViewCell { let label = UILabel() label.font = .sfProRegular24 label.textColor = .white + label.text = "맑음" return label }() @@ -34,6 +43,7 @@ class SummaryCollectionViewCell: UICollectionViewCell { let label = UILabel() label.font = .sfProMedium20 label.textColor = .white + label.text = "최대 0℃ 최소 -12℃" return label }() @@ -49,25 +59,19 @@ class SummaryCollectionViewCell: UICollectionViewCell { return stackView }() - override init(frame: CGRect) { - super.init(frame: frame) + + // MARK: - Function + // MARK: LifeCycle + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) setUI() } - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - func setData(city: String, temp: String, weather: String, maxMin: String) { - self.cityTitleLabel.text = city - self.tempTitleLabel.text = temp - self.weatherLabel.text = weather - self.maxMinTempLabel.text = maxMin - } - - + // MARK: Layout Helpers private func setUI(){ setViewHierarchy() setConstraints() @@ -78,12 +82,16 @@ class SummaryCollectionViewCell: UICollectionViewCell { } private func setConstraints() { + weatherStackView.snp.makeConstraints { $0.center.equalToSuperview() } } -} - -extension SummaryCollectionViewCell: CollectionViewCellReuseProtocol { + func setData (city: String, temp: String, weather: String, maxmin: String) { + self.cityTitleLabel.text = city + self.tempTitleLabel.text = temp + self.weatherLabel.text = weather + self.maxMinTempLabel.text = maxmin + } } diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/Helper/WeekHeaderView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/Helper/WeekHeaderView.swift new file mode 100644 index 0000000..a542643 --- /dev/null +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/Helper/WeekHeaderView.swift @@ -0,0 +1,81 @@ +// +// WeekHeaderView.swift +// SOPTWeather +// +// Created by 지희의 MAC on 12/12/23. +// + +import UIKit + +import SnapKit + +final class WeekHeaderView: UITableViewHeaderFooterView { + // MARK: - Variables + // MARK: Const + static var reuseId = "WeekHeaderView" + + // MARK: Component + private let titleLabel: UILabel = { + let label = UILabel() + label.font = .sfProMedium15 + label.text = "10일간의 일기예보" + label.textColor = .darkGray + return label + }() + + private let iconImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(systemName: "calendar")?.withTintColor(.darkGray, renderingMode: .alwaysOriginal) + return imageView + }() + + private lazy var titleStackView : UIStackView = { + let stackView = UIStackView() + stackView.axis = .horizontal + stackView.spacing = 5 + stackView.addArrangeSubViews(iconImageView, titleLabel) + return stackView + }() + + let backView: UIView = { + let view = UIView() + view.backgroundColor = .white.withAlphaComponent(0.02) + view.layer.cornerRadius = 20 + return view + }() + + + // MARK: - Function + // MARK: LifeCycle + override init(reuseIdentifier: String?) { + super.init(reuseIdentifier: reuseIdentifier) + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Layout Helpers + private func setUI(){ + setViewHierarchy() + setConstraints() + } + + private func setViewHierarchy() { + + self.addSubViews(backView, titleStackView) + } + + private func setConstraints() { + backView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.leading.trailing.equalToSuperview().inset(10) + $0.height.equalTo(675) + } + titleStackView.snp.makeConstraints { + $0.top.equalToSuperview().offset(10) + $0.leading.equalToSuperview().offset(15) + } + } +} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/SectionBackgroundView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/SectionBackgroundView.swift deleted file mode 100644 index 6c70eef..0000000 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/SectionBackgroundView.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// HeaderView.swift -// SOPTWeather -// -// Created by 지희의 MAC on 2023/11/10. -// - -import UIKit - -class SectionBackgroundView: UICollectionReusableView { - private let backgroundView: UIView = { - let view = UIView() - view.backgroundColor = .clear - view.layer.borderColor = UIColor.white.cgColor - view.layer.borderWidth = 0.5 - view.layer.cornerRadius = 15 - return view - }() - - override init(frame: CGRect) { - super.init(frame: frame) - backgroundColor = .clear - addSubview(backgroundView) - - backgroundView.snp.makeConstraints { - $0.top.bottom.equalToSuperview() - $0.leading.trailing.equalToSuperview().inset(10) - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/TimeWeatherTableViewCell.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/TimeWeatherTableViewCell.swift new file mode 100644 index 0000000..49eb173 --- /dev/null +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/TimeWeatherTableViewCell.swift @@ -0,0 +1,112 @@ +// +// TimeWeatherTableViewCell.swift +// SOPTWeather +// +// Created by 지희의 MAC on 12/12/23. +// + +import UIKit + +class TimeWeatherTableViewCell: UITableViewCell { + + static let reuseId = "TimeWeatherTableViewCell" + + let detailLabel: UILabel = { + let label = UILabel() + label.text = "08:00~09:00에 강우 상태가, 18:00에 한때 흐린 상태가 예상됩니다." + label.font = .sfProRegular18 + label.textColor = .white + label.numberOfLines = 3 + return label + }() + + let lineView: UIView = { + let view = UIView() + view.backgroundColor = .white + return view + }() + + lazy var timeWeatherCollectionView: UICollectionView = { + let flowLayout = UICollectionViewFlowLayout() + flowLayout.itemSize = CGSize(width: 44, height: 100) + flowLayout.minimumInteritemSpacing = 10 + flowLayout.scrollDirection = .horizontal + flowLayout.sectionInset = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20) + + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + collectionView.register(TimeWeatherCollectionViewCell.self, forCellWithReuseIdentifier: TimeWeatherCollectionViewCell.reuseIdentifier) + collectionView.delegate = self + collectionView.dataSource = self + collectionView.backgroundColor = .clear + collectionView.showsHorizontalScrollIndicator = false + return collectionView + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setUI() + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setUI(){ + setViewHierarchy() + setConstraints() + } + + private func setViewHierarchy() { + + self.backgroundColor = .clear + self.contentView.backgroundColor = .white.withAlphaComponent(0.03) + self.contentView.layer.cornerRadius = 20 + self.contentView.addSubViews(detailLabel, lineView, timeWeatherCollectionView) + } + + private func setConstraints() { + + self.contentView.snp.makeConstraints { + $0.leading.trailing.equalToSuperview().inset(10) + } + + detailLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(10) + $0.width.equalTo(340) + $0.centerX.equalToSuperview() + } + + lineView.snp.makeConstraints { + $0.top.equalTo(detailLabel.snp.bottom).offset(11) + $0.leading.trailing.equalToSuperview().inset(10) + $0.height.equalTo(0.2) + } + + timeWeatherCollectionView.snp.makeConstraints { + $0.top.equalTo(lineView.snp.bottom).offset(11) + $0.leading.trailing.equalToSuperview() + $0.bottom.equalToSuperview().inset(10) + $0.height.equalTo(140) + } + } + +} + +extension TimeWeatherTableViewCell: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return cityWeatherList[0].weatherinfomation.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TimeWeatherCollectionViewCell.reuseIdentifier, for: indexPath) as? TimeWeatherCollectionViewCell else { return UICollectionViewCell() } + cell.setData(time: cityWeatherList[0].weatherinfomation[indexPath.item].time, icon: cityWeatherList[0].weatherinfomation[indexPath.item].weatherIconImage, temp: cityWeatherList[0].weatherinfomation[indexPath.item].tempText) + return cell + } + + +} + +extension TimeWeatherTableViewCell: UICollectionViewDelegate { + +} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/WeeklyWeatherCollectionViewCell.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/WeekWeatherTableViewCell.swift similarity index 87% rename from SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/WeeklyWeatherCollectionViewCell.swift rename to SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/WeekWeatherTableViewCell.swift index 7457544..0fa619c 100644 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/WeeklyWeatherCollectionViewCell.swift +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Cells/WeekWeatherTableViewCell.swift @@ -1,14 +1,15 @@ // -// WeeklyWeatherCollectionViewCell.swift +// WeekWeatherTableViewCell.swift // SOPTWeather // -// Created by 지희의 MAC on 2023/11/08. +// Created by 지희의 MAC on 12/12/23. // import UIKit -class WeeklyWeatherCollectionViewCell: UICollectionViewCell { - +class WeekWeatherTableViewCell: UITableViewCell { + + static let reuseId = "WeekWeatherTableViewCell" private let weekLabel: UILabel = { let label = UILabel() @@ -67,15 +68,17 @@ class WeeklyWeatherCollectionViewCell: UICollectionViewCell { return stackView }() - override init(frame: CGRect) { - super.init(frame: frame) + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) setUI() + } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + func setData(week:String, icon: UIImage, min:String, gradient: UIImage, max: String) { weekLabel.text = week WeathericonImageView.image = icon @@ -90,6 +93,7 @@ class WeeklyWeatherCollectionViewCell: UICollectionViewCell { } private func setViewHierarchy() { + self.backgroundColor = .clear self.addSubViews(horizontalStackView) } @@ -100,7 +104,3 @@ class WeeklyWeatherCollectionViewCell: UICollectionViewCell { } } - -extension WeeklyWeatherCollectionViewCell: CollectionViewCellReuseProtocol { - -} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewControllers/CityWeatherDetailViewController.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewControllers/CityWeatherDetailViewController.swift new file mode 100644 index 0000000..8353b72 --- /dev/null +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewControllers/CityWeatherDetailViewController.swift @@ -0,0 +1,46 @@ +// +// WeatherDetailViewController.swift +// SOPTWeather +// +// Created by 지희의 MAC on 2023/10/21. +// + +import UIKit + +import SnapKit + +class CityWeatherDetailViewController: UIViewController { + + let cityWeatherDeatilView = CityWeatherView() + var weatherData = (cityName: "", weatherText: "", maxminTemp: "", weatherinfomation: []) + + override func loadView() { + super.loadView() + self.view = cityWeatherDeatilView + } + + override func viewDidLoad() { + super.viewDidLoad() + addTarget() + } + + func addTarget() { + navigationController?.interactivePopGestureRecognizer?.isEnabled = true + navigationController?.interactivePopGestureRecognizer?.delegate = self + cityWeatherDeatilView.listButton.addTarget(self, action: #selector(tapListButton), for: .touchUpInside) + } + + func setDelegate() { + cityWeatherDeatilView.weatherTableView.delegate = self + } + + @objc func tapListButton() { + navigationController?.popViewController(animated: true) + } +} + +extension CityWeatherDetailViewController : UIGestureRecognizerDelegate { } + +extension CityWeatherDetailViewController: UITableViewDelegate { } + + diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewControllers/WeatherDetailViewController.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewModel/WeatherDetailViewModel.swift similarity index 65% rename from SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewControllers/WeatherDetailViewController.swift rename to SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewModel/WeatherDetailViewModel.swift index 6654e00..ea52b19 100644 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewControllers/WeatherDetailViewController.swift +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/ViewModel/WeatherDetailViewModel.swift @@ -1,16 +1,13 @@ // -// WeatherDetailViewController.swift +// WeatherDetailViewModel.swift // SOPTWeather // -// Created by 지희의 MAC on 2023/10/21. +// Created by 지희의 MAC on 12/12/23. // import UIKit -import SnapKit - -class WeatherDetailViewController: UIViewController { - +class WeatherDetailViewModel: NSObject { let weeklyWeatherData: [DayWeatherModel] = [DayWeatherModel(day: "오늘", weatherIconImage: ImageLiterals.sunIcon.withRenderingMode(.alwaysOriginal), minTempText: "15°", maxTempText: "29°", gradientImage: ImageLiterals.todayTempGradient), DayWeatherModel(day: "월", weatherIconImage: ImageLiterals.sunIcon.withRenderingMode(.alwaysOriginal), minTempText: "18°", maxTempText: "27°", gradientImage: ImageLiterals.oneDayTempGradient), @@ -25,45 +22,38 @@ class WeatherDetailViewController: UIViewController { DayWeatherModel(day: "수", weatherIconImage: ImageLiterals.sunIcon.withRenderingMode(.alwaysOriginal), minTempText: "18°", maxTempText: "27°", gradientImage: ImageLiterals.tenDayTempGradient) ] - - let weatherDetailView = WeatherDetailView() - let cityWeatherDeatilView = CityWeatherDetailView() - var weatherData = (cityName: "", weatherText: "", maxminTemp: "", weatherinfomation: []) - - override func viewDidLoad() { - super.viewDidLoad() - setView() - addTarget() +} + +extension WeatherDetailViewModel: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch section { + case 0: + return 1 + case 1: + return 10 + default: + return 0 + } } - func setView() { - cityWeatherDeatilView.detailWeatherData = weatherData - cityWeatherDeatilView.weekWeatherData = weeklyWeatherData - - view.addSubview(cityWeatherDeatilView) - - cityWeatherDeatilView.snp.makeConstraints { - $0.edges.equalToSuperview() + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = UITableViewCell() + if indexPath.section == 0 { + guard let cell = tableView.dequeueReusableCell(withIdentifier: TimeWeatherTableViewCell.reuseId, for: indexPath) as? TimeWeatherTableViewCell else { return cell } + cell.selectionStyle = .none + return cell + } else if indexPath.section == 1 { + guard let cell = tableView.dequeueReusableCell(withIdentifier: WeekWeatherTableViewCell.reuseId, for: indexPath) as? WeekWeatherTableViewCell else { return cell } + cell.setData(week: weeklyWeatherData[indexPath.row].day , icon: weeklyWeatherData[indexPath.row].weatherIconImage, min: weeklyWeatherData[indexPath.row].minTempText, gradient: weeklyWeatherData[indexPath.row].gradientImage, max: weeklyWeatherData[indexPath.row].maxTempText) + cell.selectionStyle = .none + return cell } + return cell } - func addTarget() { - navigationController?.interactivePopGestureRecognizer?.isEnabled = true - navigationController?.interactivePopGestureRecognizer?.delegate = self - cityWeatherDeatilView.listButton.addTarget(self, action: #selector(tapListButton), for: .touchUpInside) + func numberOfSections(in tableView: UITableView) -> Int { + return 2 } - func setDelegate() { - cityWeatherDeatilView.collectionView.delegate = self - } - @objc func tapListButton() { - navigationController?.popViewController(animated: true) - } } - -extension WeatherDetailViewController : UIGestureRecognizerDelegate { } - -extension WeatherDetailViewController: UICollectionViewDelegate { } - - diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/CityWeatherDetailView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/CityWeatherDetailView.swift deleted file mode 100644 index fc64056..0000000 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/CityWeatherDetailView.swift +++ /dev/null @@ -1,262 +0,0 @@ -// -// CityWeatherDetailView.swift -// SOPTWeather -// -// Created by 지희의 MAC on 2023/11/08. -// - -import UIKit - -import SnapKit - -class CityWeatherDetailView: UIView { - // MARK: - Variables - // MARK: Property - var detailWeatherData: = (cityName: "", weatherText: "", maxminTemp: "", weatherinfomation: []) - var weekWeatherData: [DayWeatherModel] = [] - - // MARK: Component - private let backgroundImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = ImageLiterals.backgroundFull - return imageView - }() - - let mapButton: UIButton = { - let button = UIButton() - button.setImage(ImageLiterals.mapIcon, for: .normal) - button.tintColor = .white - return button - }() - - let listButton: UIButton = { - let button = UIButton() - button.setImage(ImageLiterals.listIcon, for: .normal) - button.tintColor = .white - return button - }() - - lazy var collectionView: UICollectionView = { - let flowLayout = createLayout() - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) - collectionView.isScrollEnabled = true - collectionView.backgroundColor = .clear - - flowLayout.register(SectionBackgroundView.self, forDecorationViewOfKind: "background") - - collectionView.register(HeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: HeaderView.reuseId) - - SummaryCollectionViewCell.register(collectionView: collectionView) - TimeWeatherCollectionViewCell.register(collectionView: collectionView) - WeeklyWeatherCollectionViewCell.register(collectionView: collectionView) - collectionView.dataSource = self - return collectionView - }() - - override init(frame: CGRect) { - super.init(frame: frame) - setUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - private func setUI(){ - setViewHierarchy() - setConstraints() - } - - private func setViewHierarchy() { - self.addSubViews(backgroundImageView, - collectionView, - mapButton, - listButton) - } - - private func setConstraints() { - - backgroundImageView.snp.makeConstraints { - $0.edges.equalToSuperview() - } - - collectionView.snp.makeConstraints { - $0.top.equalToSuperview().offset(34) - $0.leading.trailing.bottom.equalToSuperview() - } - - mapButton.snp.makeConstraints { - $0.bottom.equalToSuperview().inset(34) - $0.leading.equalToSuperview().inset(16) - } - - listButton.snp.makeConstraints { - $0.bottom.equalToSuperview().inset(34) - $0.trailing.equalToSuperview().inset(16) - } - } - - private func createMainLayout() -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(self.bounds.width), heightDimension: .absolute(212)) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - // group설정 - let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(250)) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: 1) - // section 설정 - let section = NSCollectionLayoutSection(group: group) - //스크롤 설정 - section.orthogonalScrollingBehavior = .paging - - return section - } - - - private func createTimeLayout() -> NSCollectionLayoutSection { - // 각 item의 사이즈 설정 - let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(44), heightDimension: .absolute(122)) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - item.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 34, bottom: 10, trailing: 34) - - // group설정 - let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(144)) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - group.interItemSpacing = NSCollectionLayoutSpacing.fixed(22) - - // section 설정 - let section = NSCollectionLayoutSection(group: group) - - - //헤더뷰 레이아웃 - section.boundarySupplementaryItems = [.init(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading)] - section.contentInsets = NSDirectionalEdgeInsets(top: 20, leading: 24, bottom: 20, trailing: 24) - - - // Background - let sectionBackgroundDecoration = NSCollectionLayoutDecorationItem.background(elementKind: "background") - section.decorationItems = [sectionBackgroundDecoration] - - //스크롤 설정 - section.orthogonalScrollingBehavior = .continuous - - return section - } - - private func createWeekLayout() -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize(widthDimension: .absolute(self.bounds.width), heightDimension: .absolute(55)) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalHeight(1)) - let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item]) - - let section = NSCollectionLayoutSection(group: group) - - section.orthogonalScrollingBehavior = .none - - section.boundarySupplementaryItems = [.init(layoutSize: .init(widthDimension: .fractionalWidth(1), heightDimension: .absolute(50)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading)] - - return section - } - - private func createLayout() -> UICollectionViewCompositionalLayout { - return UICollectionViewCompositionalLayout {[weak self] sectionNumber, env -> NSCollectionLayoutSection? in - - switch sectionNumber { - case 0: - return self?.createMainLayout() - case 1: - return self?.createTimeLayout() - default: - return self?.createWeekLayout() - } - } - } - -} - -extension CityWeatherDetailView: UICollectionViewDataSource { - - func numberOfSections(in collectionView: UICollectionView) -> Int { - return 3 - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - switch section { - case 0: - return 1 - case 1: - return detailWeatherData.weatherinfomation.count - case 2: - return weekWeatherData.count - default: - return 1 - } - } - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - if indexPath.section == 0 { - let cell = SummaryCollectionViewCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) - - cell.setData(city: detailWeatherData.cityName, temp: detailWeatherData.weatherinfomation[0].tempText, weather: detailWeatherData.weatherText, maxMin: detailWeatherData.maxminTemp) - return cell - - } else if indexPath.section == 1 { - let cell = TimeWeatherCollectionViewCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) - cell.setData(time: detailWeatherData.weatherinfomation[indexPath.row].time, - icon: detailWeatherData.weatherinfomation[indexPath.row].weatherIconImage, - temp: detailWeatherData.weatherinfomation[indexPath.row].tempText) - return cell - - } else if indexPath.section == 2 { - let cell = WeeklyWeatherCollectionViewCell.dequeueReusableCell(collectionView: collectionView, indexPath: indexPath) - cell.setData(week: weekWeatherData[indexPath.row].day, icon: weekWeatherData[indexPath.row].weatherIconImage, min: weekWeatherData[indexPath.row].minTempText, gradient: weekWeatherData[indexPath.row].gradientImage, max: weekWeatherData[indexPath.row].maxTempText) - return cell - } - - return UICollectionViewCell() - } - - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - if kind == UICollectionView.elementKindSectionHeader { - let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: HeaderView.reuseId, for: indexPath) as! HeaderView - if indexPath.section == 1 { - - let label = UILabel() - label.text = "08:00~09:00에 강우 상태가, 18:00에 한때 흐린 상태가 예상됩니다." - label.font = .sfProRegular18 - label.textColor = .white - label.numberOfLines = 3 - - headerView.addSubview(label) - - // 레이블의 제약조건 설정 (예: 스냅킷 라이브러리 사용) - label.snp.makeConstraints { - $0.top.equalToSuperview().inset(15) - $0.leading.trailing.equalToSuperview().inset(16) - } - - } else if indexPath.section == 2 { - let iconImageView = UIImageView(image: UIImage(systemName: "calendar")?.withTintColor(.white.withAlphaComponent(0.3), renderingMode: .alwaysOriginal)) - - - let label = UILabel() - label.text = "10일간의 일기예보" - label.font = .sfProMedium15 - label.textColor = .white.withAlphaComponent(0.3) - headerView.addSubViews(iconImageView, label) - - iconImageView.snp.makeConstraints { - $0.top.equalToSuperview().offset(15) - $0.leading.equalToSuperview().offset(15) - $0.size.equalTo(24) - } - - label.snp.makeConstraints { - $0.centerY.equalTo(iconImageView) - $0.leading.equalTo(iconImageView.snp.trailing).offset(5) - } - } - return headerView - } - return UICollectionReusableView() - } - -} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/CityWeatherView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/CityWeatherView.swift new file mode 100644 index 0000000..144505c --- /dev/null +++ b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/CityWeatherView.swift @@ -0,0 +1,157 @@ +// +// WeatherDetail.swift +// SOPTWeather +// +// Created by 지희의 MAC on 12/11/23. +// + +import UIKit + +import SnapKit + +final class CityWeatherView: UIView { + // MARK: - Variables + // MARK: Constants + let viewModel = WeatherDetailViewModel() + + // MARK: Property + + + // MARK: Component + private let backgroundImageView: UIImageView = { + let imageView = UIImageView() + imageView.image = ImageLiterals.backgroundFull + return imageView + }() + + let mapButton: UIButton = { + let button = UIButton() + button.setImage(ImageLiterals.mapIcon, for: .normal) + button.tintColor = .white + return button + }() + + let listButton: UIButton = { + let button = UIButton() + button.setImage(ImageLiterals.listIcon, for: .normal) + button.tintColor = .white + return button + }() + + lazy var weatherTableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .grouped) + tableView.backgroundColor = .clear + tableView.register(WeatherTimeHeaderView.self, forHeaderFooterViewReuseIdentifier: WeatherTimeHeaderView.reuseId) + tableView.register(WeekHeaderView.self, forHeaderFooterViewReuseIdentifier: WeekHeaderView.reuseId) + tableView.register(TimeWeatherTableViewCell.self, forCellReuseIdentifier: TimeWeatherTableViewCell.reuseId) + tableView.register(WeekWeatherTableViewCell.self, forCellReuseIdentifier: WeekWeatherTableViewCell.reuseId) + return tableView + }() + + // MARK: - Functio + // MARK: LifeCycle + override init(frame: CGRect) { + super.init(frame: frame) + setUI() + bindViewModel() + setDelegate() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: Layout Helpers + private func setUI(){ + setViewHierarchy() + setConstraints() + } + + private func setViewHierarchy() { + self.addSubViews(backgroundImageView, weatherTableView, listButton, mapButton) + } + + private func setConstraints() { + backgroundImageView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + weatherTableView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + + listButton.snp.makeConstraints { + $0.leading.equalToSuperview().offset(28) + $0.bottom.equalToSuperview().inset(32) + } + + mapButton.snp.makeConstraints { + $0.trailing.equalToSuperview().inset(28) + $0.bottom.equalTo(listButton.snp.bottom) + } + } + + // MARK: Custom Function + func setDelegate() { + self.weatherTableView.delegate = self + } + + func bindViewModel() { + self.weatherTableView.dataSource = viewModel + } + + // MARK: Objc Function + + +} + + +extension CityWeatherView: UITableViewDelegate { + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + switch section { + case 0: + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: WeatherTimeHeaderView.reuseId) as? WeatherTimeHeaderView + return header + case 1: + let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: WeekHeaderView.reuseId) as? WeekHeaderView + return header + default: + let header = UIView() + return header + } + + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + switch section { + case 0: + return 280 + case 1: + return 40 + default: + return 0 + } + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + switch indexPath.section { + case 0: + return 212 + case 1: + return 55 + default: + return 0 + } + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + switch section { + case 0 : + return 30 + case 1: + return 10 + default: + return 0 + } + } +} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/TimeWeatherView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/TimeWeatherView.swift deleted file mode 100644 index e5a936a..0000000 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/TimeWeatherView.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// TimeWeatherView.swift -// SOPTWeather -// -// Created by 지희의 MAC on 2023/10/26. -// - -import UIKit - -import SnapKit - -class TimeWeatherView: UIView { - // MARK: - Variables - // MARK: Constants - - // MARK: Property - - // MARK: Component - private let timeLabel: UILabel = { - let label = UILabel() - label.font = .sfProMedium17 - label.textColor = .white - return label - }() - - private let iconImageView: UIImageView = { - let imageView = UIImageView() - imageView.tintColor = .white - imageView.contentMode = .scaleAspectFit - return imageView - }() - - private let tempLabel: UILabel = { - let label = UILabel() - label.font = .sfProMedium22 - label.textColor = .white - return label - }() - - - override init(frame: CGRect) { - super.init(frame: frame) - setUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - init(time: String, iconImage: UIImage, temp: String) { - super.init(frame: .zero) - self.timeLabel.text = time - self.iconImageView.image = iconImage.withAlignmentRectInsets(UIEdgeInsets(top: -10, left: -10, bottom: -10, right: -10)) - self.tempLabel.text = temp - setUI() - } - - private func setUI(){ - setViewHierarchy() - setConstraints() - } - - private func setViewHierarchy() { - self.addSubViews(timeLabel,iconImageView,tempLabel) - } - - private func setConstraints() { - self.snp.makeConstraints { - $0.width.equalTo(44) - } - - timeLabel.snp.makeConstraints { - $0.top.equalToSuperview() - $0.centerX.equalToSuperview() - } - - iconImageView.snp.makeConstraints { - $0.top.equalTo(timeLabel.snp.bottom).offset(14) - $0.centerX.equalToSuperview() - $0.size.equalTo(44) - } - - tempLabel.snp.makeConstraints { - $0.top.equalTo(iconImageView.snp.bottom).offset(14) - $0.centerX.equalToSuperview() - } - } -} diff --git a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/WeatherDetailView.swift b/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/WeatherDetailView.swift deleted file mode 100644 index 4f60a75..0000000 --- a/SOPTWeather/SOPTWeather/Presentation/WeatherDetail/Views/WeatherDetailView.swift +++ /dev/null @@ -1,226 +0,0 @@ -// -// WeatherDetailView.swift -// SOPTWeather -// -// Created by 지희의 MAC on 2023/10/21. -// - -import UIKit -import SnapKit - -class WeatherDetailView: UIView { - - // MARK: - Variables - // MARK: Property - private var detailWeatherList: [TimeWeatherModel] = [] - - // MARK: Component - let scrollView: UIScrollView = { - let scrollView = UIScrollView() - scrollView.alwaysBounceVertical = true - return scrollView - }() - - private let backgroundImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = ImageLiterals.backgroundFull - return imageView - }() - - private let cityTitleLabel: UILabel = { - let label = UILabel() - label.font = .sfProRegular36 - label.textColor = .white - return label - }() - - private let tempTitleLabel: UILabel = { - let label = UILabel() - label.font = .sfProThin102 - label.textColor = .white - return label - }() - - private let weatherLabel: UILabel = { - let label = UILabel() - label.font = .sfProRegular24 - label.textColor = .white - return label - }() - - private let maxMinTempLabel: UILabel = { - let label = UILabel() - label.font = .sfProMedium20 - label.textColor = .white - return label - }() - - private lazy var weatherStackView : UIStackView = { - let stackView = UIStackView() - stackView.addArrangeSubViews(cityTitleLabel, - tempTitleLabel, - weatherLabel, - maxMinTempLabel) - stackView.spacing = 4 - stackView.alignment = .center - stackView.axis = .vertical - return stackView - }() - - - private let weatherDetailLabel: UILabel = { - let label = UILabel() - label.font = .sfProRegular18 - label.textColor = .white - label.numberOfLines = 3 - label.text = "08:00~09:00에 강우 상태가, 18:00에 한때 흐린 상태가 예상됩니다." - return label - }() - - private let lineView: UIView = { - let view = UIView() - view.backgroundColor = .darkGray - return view - }() - - private let timeWeatherScrollView: UIScrollView = { - let scrollView = UIScrollView() - scrollView.showsHorizontalScrollIndicator = false - return scrollView - }() - - private lazy var weatherDetailStackView: UIStackView = { - let stackView = UIStackView() - stackView.axis = .vertical - stackView.spacing = 11 - stackView.addArrangeSubViews(weatherDetailLabel, - lineView, - timeWeatherScrollView) - stackView.layer.cornerRadius = 15 - stackView.layer.borderWidth = 1 - stackView.layer.borderColor = UIColor.darkGray.cgColor - stackView.isLayoutMarginsRelativeArrangement = true - stackView.layoutMargins = UIEdgeInsets(top: 10, left: 15, bottom: 10, right: 15) - stackView.frame = CGRect(x: 0, y: 0, width: 335, height: 212) - - return stackView - }() - - private lazy var timeStackView: UIStackView = { - let stackView = UIStackView() - stackView.spacing = 22 - return stackView - }() - - let mapButton: UIButton = { - let button = UIButton() - button.setImage(ImageLiterals.mapIcon, for: .normal) - button.tintColor = .white - return button - }() - - let listButton: UIButton = { - let button = UIButton() - button.setImage(ImageLiterals.listIcon, for: .normal) - button.tintColor = .white - return button - }() - - - // MARK: - Function - // MARK: Init - override init(frame: CGRect) { - super.init(frame: frame) - setUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: Layout Helpers - private func setUI(){ - setViewHierarchy() - setConstraints() - } - - private func setViewHierarchy() { - - self.addSubViews(backgroundImageView,scrollView, - mapButton, - listButton) - - scrollView.addSubViews(weatherStackView, - weatherDetailStackView) - - timeWeatherScrollView.addSubview(timeStackView) - - for i in 0..