diff --git a/Weather-App/Weather-App/AppDelegate.swift b/Weather-App/Weather-App/AppDelegate.swift
index 1c0f960..e518dac 100644
--- a/Weather-App/Weather-App/AppDelegate.swift
+++ b/Weather-App/Weather-App/AppDelegate.swift
@@ -13,7 +13,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
- // Override point for customization after application launch.
+
+ /* ========= 폰트 이름 확인 =========
+ for family in UIFont.familyNames {
+ // 폰트 패밀리 이름
+ print(family)
+ // 각 폰트 이름
+ for names in UIFont.fontNames(forFamilyName: family) {
+ print("== \(names)")
+ }
+ }
+ =============================== */
+
return true
}
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/bar1.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/bar1.imageset/Contents.json
new file mode 100644
index 0000000..64469ff
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar1.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bar1.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/Assets.xcassets/bars/bar1.imageset/bar1.svg b/Weather-App/Weather-App/Assets.xcassets/bars/bar1.imageset/bar1.svg
new file mode 100644
index 0000000..ca082e1
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar1.imageset/bar1.svg
@@ -0,0 +1,11 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/bar2.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/bar2.imageset/Contents.json
new file mode 100644
index 0000000..4509b8a
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar2.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bar2.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/Assets.xcassets/bars/bar2.imageset/bar2.svg b/Weather-App/Weather-App/Assets.xcassets/bars/bar2.imageset/bar2.svg
new file mode 100644
index 0000000..3da9742
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar2.imageset/bar2.svg
@@ -0,0 +1,10 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/bar3.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/bar3.imageset/Contents.json
new file mode 100644
index 0000000..62bf09e
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar3.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bar3.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/Assets.xcassets/bars/bar3.imageset/bar3.svg b/Weather-App/Weather-App/Assets.xcassets/bars/bar3.imageset/bar3.svg
new file mode 100644
index 0000000..46862b2
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar3.imageset/bar3.svg
@@ -0,0 +1,10 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/bar4.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/bar4.imageset/Contents.json
new file mode 100644
index 0000000..63fd5a0
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar4.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bar4.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/Assets.xcassets/bars/bar4.imageset/bar4.svg b/Weather-App/Weather-App/Assets.xcassets/bars/bar4.imageset/bar4.svg
new file mode 100644
index 0000000..90fa37f
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar4.imageset/bar4.svg
@@ -0,0 +1,10 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/bar5.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/bar5.imageset/Contents.json
new file mode 100644
index 0000000..3e10651
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar5.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bar5.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/Assets.xcassets/bars/bar5.imageset/bar5.svg b/Weather-App/Weather-App/Assets.xcassets/bars/bar5.imageset/bar5.svg
new file mode 100644
index 0000000..acb7e49
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar5.imageset/bar5.svg
@@ -0,0 +1,10 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/bars/bar6.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/bars/bar6.imageset/Contents.json
new file mode 100644
index 0000000..b37d570
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar6.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "bar6.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/Assets.xcassets/bars/bar6.imageset/bar6.svg b/Weather-App/Weather-App/Assets.xcassets/bars/bar6.imageset/bar6.svg
new file mode 100644
index 0000000..2326c92
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/bars/bar6.imageset/bar6.svg
@@ -0,0 +1,17 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/icons/calendar.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/icons/calendar.imageset/Contents.json
new file mode 100644
index 0000000..14f6363
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/icons/calendar.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "calendar.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/Assets.xcassets/icons/calendar.imageset/calendar.svg b/Weather-App/Weather-App/Assets.xcassets/icons/calendar.imageset/calendar.svg
new file mode 100644
index 0000000..8bbd88f
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/icons/calendar.imageset/calendar.svg
@@ -0,0 +1,3 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Contents.json
index 00d2a45..1fb746a 100644
--- a/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Contents.json
+++ b/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Contents.json
@@ -1,7 +1,7 @@
{
"images" : [
{
- "filename" : "Frame 8.svg",
+ "filename" : "search.svg",
"idiom" : "universal",
"scale" : "1x"
},
diff --git a/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Frame 8.svg b/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Frame 8.svg
deleted file mode 100644
index b100ed1..0000000
--- a/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/Frame 8.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/search.svg b/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/search.svg
new file mode 100644
index 0000000..b66dd1f
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/icons/icon-search.imageset/search.svg
@@ -0,0 +1,3 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/icons/pageIcon.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/icons/pageIcon.imageset/Contents.json
new file mode 100644
index 0000000..484dbac
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/icons/pageIcon.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "pageIcon.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/Assets.xcassets/icons/pageIcon.imageset/pageIcon.svg b/Weather-App/Weather-App/Assets.xcassets/icons/pageIcon.imageset/pageIcon.svg
new file mode 100644
index 0000000..6b78f87
--- /dev/null
+++ b/Weather-App/Weather-App/Assets.xcassets/icons/pageIcon.imageset/pageIcon.svg
@@ -0,0 +1,4 @@
+
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/Contents.json b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/Contents.json
index 5cbb56e..34d04b2 100644
--- a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/Contents.json
+++ b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/Contents.json
@@ -1,17 +1,17 @@
{
"images" : [
{
- "filename" : "bg@1.png",
+ "filename" : "bg-long.jpg",
"idiom" : "universal",
"scale" : "1x"
},
{
- "filename" : "bg@2.png",
+ "filename" : "bg-long@2.jpg",
"idiom" : "universal",
"scale" : "2x"
},
{
- "filename" : "bg@3.png",
+ "filename" : "bg-long@3.jpg",
"idiom" : "universal",
"scale" : "3x"
}
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long.jpg b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long.jpg
new file mode 100644
index 0000000..ed3928b
Binary files /dev/null and b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long.jpg differ
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long@2.jpg b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long@2.jpg
new file mode 100644
index 0000000..c307fd2
Binary files /dev/null and b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long@2.jpg differ
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long@3.jpg b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long@3.jpg
new file mode 100644
index 0000000..1dcc874
Binary files /dev/null and b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg-long@3.jpg differ
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@1.png b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@1.png
deleted file mode 100644
index 27aa323..0000000
Binary files a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@1.png and /dev/null differ
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@2.png b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@2.png
deleted file mode 100644
index a678eb0..0000000
Binary files a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@2.png and /dev/null differ
diff --git a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@3.png b/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@3.png
deleted file mode 100644
index 9cfe37b..0000000
Binary files a/Weather-App/Weather-App/Assets.xcassets/images/bg.imageset/bg@3.png and /dev/null differ
diff --git a/Weather-App/Weather-App/Info.plist b/Weather-App/Weather-App/Info.plist
index 08e2c3a..8564edb 100644
--- a/Weather-App/Weather-App/Info.plist
+++ b/Weather-App/Weather-App/Info.plist
@@ -2,13 +2,15 @@
+ UIViewControllerBasedStatusBarAppearance
+
UIAppFonts
- SF-Pro-Text-Thin.otf
- SF-Pro-Text-Regular.otf
- SF-Pro-Text-Medium.otf
SF-Pro-Text-Bold.otf
SF-Pro-Text-Light.otf
+ SF-Pro-Text-Medium.otf
+ SF-Pro-Text-Regular.otf
+ SF-Pro-Text-Thin.otf
UIApplicationSceneManifest
diff --git a/Weather-App/Weather-App/Model/WeatherResponse.swift b/Weather-App/Weather-App/Model/WeatherResponse.swift
new file mode 100644
index 0000000..5b9d9f7
--- /dev/null
+++ b/Weather-App/Weather-App/Model/WeatherResponse.swift
@@ -0,0 +1,24 @@
+//
+// Weather.swift
+// Weathers
+//
+// Created by yeonsu on 11/14/23.
+//
+
+import Foundation
+
+struct WeatherResponse: Codable {
+ let main: Main
+ let timezone: Int
+ let name: String
+}
+
+struct Main: Codable {
+ let temp: Double?
+ let temp_min: Double?
+ let temp_max: Double?
+
+ static var placeholder: Main {
+ return Main(temp: nil, temp_min: nil, temp_max: nil)
+ }
+}
diff --git a/Weather-App/Weather-App/Resources/Data/TodayWeatherData.swift b/Weather-App/Weather-App/Resources/Data/TodayWeatherData.swift
new file mode 100644
index 0000000..a47dcf3
--- /dev/null
+++ b/Weather-App/Weather-App/Resources/Data/TodayWeatherData.swift
@@ -0,0 +1,53 @@
+//
+// TodayWeatherData.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import Foundation
+
+struct TodayWeatherData {
+ let time: String
+ let weatherImage: String
+ let hourlyWeather: String
+
+ init(time: String, weatherImage: String, hourlyWeather: String) {
+ self.time = time
+ self.weatherImage = weatherImage
+ self.hourlyWeather = hourlyWeather
+ }
+}
+
+private let weatherImages: [String] = ["status-blur", "status-drizzle", "status-rain", "status-thunder", "status-shower"]
+
+var todayWeatherData: [TodayWeatherData] = [.init(time: "Now",
+ weatherImage: weatherImages[0],
+ hourlyWeather: "21°"),
+ .init(time: "10시",
+ weatherImage: weatherImages[1],
+ hourlyWeather: "21°"),
+ .init(time: "11시",
+ weatherImage: weatherImages[2],
+ hourlyWeather: "19°"),
+ .init(time: "12시",
+ weatherImage: weatherImages[3],
+ hourlyWeather: "19°"),
+ .init(time: "1시",
+ weatherImage: weatherImages[4],
+ hourlyWeather: "19°"),
+ .init(time: "2시",
+ weatherImage: weatherImages[4],
+ hourlyWeather: "20°"),
+ .init(time: "3시",
+ weatherImage: weatherImages[4],
+ hourlyWeather: "20°"),
+ .init(time: "4시",
+ weatherImage: weatherImages[3],
+ hourlyWeather: "18°"),
+ .init(time: "5시",
+ weatherImage: weatherImages[2],
+ hourlyWeather: "18°"),
+ .init(time: "6시",
+ weatherImage: weatherImages[0],
+ hourlyWeather: "19°")]
diff --git a/Weather-App/Weather-App/Resources/Data/WeeklyWeatherData.swift b/Weather-App/Weather-App/Resources/Data/WeeklyWeatherData.swift
new file mode 100644
index 0000000..338ae2b
--- /dev/null
+++ b/Weather-App/Weather-App/Resources/Data/WeeklyWeatherData.swift
@@ -0,0 +1,98 @@
+//
+// DetailWeatherData.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import Foundation
+
+struct WeeklyWeatherData {
+ let weekDay: String
+ let weatherImage: String
+ let chanceOfPrecipitation: String?
+ let minimumTemperature: String
+ let maximumTemperature: String
+ let temperatureBarImage: String
+
+ init(weekDay: String, weatherImage: String, chanceOfPrecipitation: String?, minimumTemperature: String, temperatureBarImage: String, maximumTemperature: String) {
+ self.weekDay = weekDay
+ self.weatherImage = weatherImage
+ self.chanceOfPrecipitation = chanceOfPrecipitation
+ self.minimumTemperature = minimumTemperature
+ self.maximumTemperature = maximumTemperature
+ self.temperatureBarImage = temperatureBarImage
+
+ }
+}
+
+private let weatherImages: [String] = ["status-blur", "status-drizzle", "status-rain", "status-thunder", "status-shower"]
+
+private let temperatureBarImages: [String] = ["bar1", "bar2", "bar3", "bar4", "bar5", "bar6"]
+
+var weeklyWeatherData: [WeeklyWeatherData] = [.init(weekDay: "오늘",
+ weatherImage: weatherImages[0],
+ chanceOfPrecipitation: nil,
+ minimumTemperature: "15°",
+ temperatureBarImage: "bar1",
+ maximumTemperature: "29°"),
+ .init(weekDay: "월",
+ weatherImage: weatherImages[1],
+ chanceOfPrecipitation: "60%",
+ minimumTemperature: "18°",
+ temperatureBarImage: "bar2",
+ maximumTemperature: "27°"),
+ .init(weekDay: "화",
+ weatherImage: weatherImages[1],
+ chanceOfPrecipitation: "60%",
+ minimumTemperature: "20°",
+ temperatureBarImage: "bar3",
+ maximumTemperature: "25°"),
+ .init(weekDay: "수",
+ weatherImage: weatherImages[1],
+ chanceOfPrecipitation: "70%",
+ minimumTemperature: "17°",
+ temperatureBarImage: "bar4",
+ maximumTemperature: "25°"),
+ .init(weekDay: "목",
+ weatherImage: weatherImages[1],
+ chanceOfPrecipitation: "50%",
+ minimumTemperature: "17°",
+ temperatureBarImage: "bar5",
+ maximumTemperature: "25°"),
+ .init(weekDay: "금",
+ weatherImage: weatherImages[2],
+ chanceOfPrecipitation: nil,
+ minimumTemperature: "20°",
+ temperatureBarImage: "bar6",
+ maximumTemperature: "26°"),
+ .init(weekDay: "토",
+ weatherImage: weatherImages[0],
+ chanceOfPrecipitation: nil,
+ minimumTemperature: "18°",
+ temperatureBarImage: "bar4",
+ maximumTemperature: "25°"),
+ .init(weekDay: "일",
+ weatherImage: weatherImages[1],
+ chanceOfPrecipitation: "50%",
+ minimumTemperature: "13°",
+ temperatureBarImage: "bar2",
+ maximumTemperature: "21°"),
+ .init(weekDay: "월",
+ weatherImage: weatherImages[1],
+ chanceOfPrecipitation: "50%",
+ minimumTemperature: "12°",
+ temperatureBarImage: "bar4",
+ maximumTemperature: "19°"),
+ .init(weekDay: "화",
+ weatherImage: weatherImages[0],
+ chanceOfPrecipitation: nil,
+ minimumTemperature: "18°",
+ temperatureBarImage: "bar3",
+ maximumTemperature: "25°"),
+ .init(weekDay: "수",
+ weatherImage: weatherImages[0],
+ chanceOfPrecipitation: nil,
+ minimumTemperature: "18°",
+ temperatureBarImage: "bar5",
+ maximumTemperature: "25°")]
diff --git a/Weather-App/Weather-App/Resources/Extension/Extension+UIView.swift b/Weather-App/Weather-App/Resources/Extension/Extension+UIView.swift
new file mode 100644
index 0000000..b1024d4
--- /dev/null
+++ b/Weather-App/Weather-App/Resources/Extension/Extension+UIView.swift
@@ -0,0 +1,17 @@
+//
+// Extension+UIView.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import UIKit
+
+extension UIView {
+ func addSubviews(_ views: UIView...) {
+ views.forEach {
+ $0.translatesAutoresizingMaskIntoConstraints = false
+ addSubview($0)
+ }
+ }
+}
diff --git a/Weather-App/Weather-App/Fonts/SF-Pro-Text-Bold.otf b/Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Bold.otf
similarity index 100%
rename from Weather-App/Weather-App/Fonts/SF-Pro-Text-Bold.otf
rename to Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Bold.otf
diff --git a/Weather-App/Weather-App/Fonts/SF-Pro-Text-Light.otf b/Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Light.otf
similarity index 100%
rename from Weather-App/Weather-App/Fonts/SF-Pro-Text-Light.otf
rename to Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Light.otf
diff --git a/Weather-App/Weather-App/Fonts/SF-Pro-Text-Medium.otf b/Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Medium.otf
similarity index 100%
rename from Weather-App/Weather-App/Fonts/SF-Pro-Text-Medium.otf
rename to Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Medium.otf
diff --git a/Weather-App/Weather-App/Fonts/SF-Pro-Text-Regular.otf b/Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Regular.otf
similarity index 100%
rename from Weather-App/Weather-App/Fonts/SF-Pro-Text-Regular.otf
rename to Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Regular.otf
diff --git a/Weather-App/Weather-App/Fonts/SF-Pro-Text-Thin.otf b/Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Thin.otf
similarity index 100%
rename from Weather-App/Weather-App/Fonts/SF-Pro-Text-Thin.otf
rename to Weather-App/Weather-App/Resources/Fonts/SF-Pro-Text-Thin.otf
diff --git a/Weather-App/Weather-App/SceneDelegate.swift b/Weather-App/Weather-App/SceneDelegate.swift
index 9ba8877..852059a 100644
--- a/Weather-App/Weather-App/SceneDelegate.swift
+++ b/Weather-App/Weather-App/SceneDelegate.swift
@@ -18,10 +18,15 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
- let window = UIWindow(windowScene: windowScene)
- window.rootViewController = ViewController()
- window.makeKeyAndVisible()
- self.window = window
+// let window = UIWindow(windowScene: windowScene)
+// window.rootViewController = ListViewController()
+// window.makeKeyAndVisible()
+// self.window = window
+
+ window = UIWindow(windowScene: windowScene)
+ let mainViewController = UINavigationController(rootViewController: ListViewController())
+ window?.rootViewController = mainViewController
+ window?.makeKeyAndVisible()
}
func sceneDidDisconnect(_ scene: UIScene) {
diff --git a/Weather-App/Weather-App/Service/GetWeatherService.swift b/Weather-App/Weather-App/Service/GetWeatherService.swift
new file mode 100644
index 0000000..911715e
--- /dev/null
+++ b/Weather-App/Weather-App/Service/GetWeatherService.swift
@@ -0,0 +1,26 @@
+//
+// GetWeatherService.swift
+// Weathers
+//
+// Created by yeonsu on 11/14/23.
+//
+
+import Foundation
+import Combine
+
+class GetWeatherService {
+ func fetchWeather(city: String) -> AnyPublisher {
+ guard let url = URL(string: Constants.URLs.weather(city: city)) else {
+ fatalError("Invalid")
+ }
+
+ return URLSession.shared.dataTaskPublisher(for: url)
+ .catch { error in
+ return Fail(error: error).eraseToAnyPublisher()
+ }
+ .map { $0.data }
+ .decode(type: WeatherResponse.self, decoder: JSONDecoder())
+ .receive(on: RunLoop.main)
+ .eraseToAnyPublisher()
+ }
+}
diff --git a/Weather-App/Weather-App/Util/Constants.swift b/Weather-App/Weather-App/Util/Constants.swift
new file mode 100644
index 0000000..b55435c
--- /dev/null
+++ b/Weather-App/Weather-App/Util/Constants.swift
@@ -0,0 +1,17 @@
+//
+// Constants.swift
+// Weathers
+//
+// Created by yeonsu on 11/14/23.
+//
+
+import Foundation
+
+struct Constants {
+ struct URLs {
+ static func weather(city: String) -> String {
+ return "https://api.openweathermap.org/data/2.5/weather?q=\(city)&appid=a4766e8295d121a9d02bf06520ee56b6&units=metric"
+ }
+ static let weather = "https://api.openweathermap.org/data/2.5/weather?q=Seoul&appid=a4766e8295d121a9d02bf06520ee56b6&units=metric"
+ }
+}
diff --git a/Weather-App/Weather-App/ViewController.swift b/Weather-App/Weather-App/ViewController.swift
deleted file mode 100644
index 467525d..0000000
--- a/Weather-App/Weather-App/ViewController.swift
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-// ViewController.swift
-// Weather-App
-//
-// Created by yeonsu on 10/19/23.
-//
-
-import UIKit
-
-class ViewController: UIViewController {
-
- override func viewDidLoad() {
- super.viewDidLoad()
- // Do any additional setup after loading the view.
-
- self.view.backgroundColor = .red
- }
-
-
-}
-
diff --git a/Weather-App/Weather-App/Views/Detail/DetailViewController.swift b/Weather-App/Weather-App/Views/Detail/DetailViewController.swift
new file mode 100644
index 0000000..51491ea
--- /dev/null
+++ b/Weather-App/Weather-App/Views/Detail/DetailViewController.swift
@@ -0,0 +1,342 @@
+//
+// DetailViewController.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import UIKit
+import Then
+import SnapKit
+
+class DetailViewController: UIViewController {
+
+ let scrollView = UIScrollView()
+ var contentView = UIView()
+
+ private let locationText = UILabel().then {
+ let customFont = UIFont(name: "SFProText-Medium", size: 36.0)
+ $0.font = customFont
+ $0.text = "의정부시"
+ $0.textColor = .white
+ }
+
+ private let degreeNumber = UILabel().then {
+ let customDegreeFont = UIFont(name: "SFProText-Thin", size: 102.0)
+ $0.font = customDegreeFont
+ $0.text = "21°"
+ $0.textColor = .white
+ }
+
+ private let statusText = UILabel().then {
+ let customStatusFont = UIFont(name: "SFProText-Regular", size: 24.0)
+ $0.font = customStatusFont
+ $0.text = "흐림"
+ $0.textColor = .white
+ }
+
+ private let todayDegreeNumber = UILabel().then {
+ let customStatusFont = UIFont(name: "SFProText-Medium", size: 20.0)
+ $0.font = customStatusFont
+ $0.text = "최고:29° 최저:15°"
+ $0.textColor = .white
+ }
+
+ private let descriptionView = UIView().then {
+ $0.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.01)
+ $0.layer.borderColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.05).cgColor
+ $0.layer.borderWidth = 1
+ $0.layer.cornerRadius = 15
+ }
+
+ private var todayWeatherScriptLabel = UILabel().then {
+ $0.textColor = .white
+ $0.text = "08:00~09:00에 강우 상태가, 18:00에 한때 흐린 상태가 예상됩니다."
+ $0.font = UIFont(name: "SFProText-Regular", size: 18)
+ $0.numberOfLines = 0
+ }
+
+ private var lineView = UIView().then {
+ $0.backgroundColor = UIColor.white
+ }
+
+ private let horizontalCollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()).then {
+ $0.backgroundColor = .clear
+ }
+
+ private let weeklyWeatherView = UIView().then {
+ $0.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.03)
+ $0.layer.borderColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.05).cgColor
+ $0.layer.borderWidth = 1
+ $0.layer.cornerRadius = 15
+ }
+
+ private let calendarIcon = UIImageView().then {
+ $0.image = UIImage(systemName: "calendar")
+ $0.tintColor = UIColor(white: 1, alpha: 0.3)
+ }
+
+ private let weeklyWeatherSubTitle = UILabel().then {
+ $0.font = UIFont(name: "SFProText-Regular", size: 15)
+ $0.text = "10일간의 일기예보"
+ $0.textColor = .white.withAlphaComponent(0.3)
+ }
+
+ private let weeklyWeatherTableView = UITableView(frame: .zero, style: .plain).then {
+ $0.backgroundColor = .clear
+ $0.separatorStyle = .singleLine
+ $0.separatorColor = .white
+ $0.isScrollEnabled = false
+ }
+
+ private let mapiconView = UIButton().then {
+ let imageView = UIImage(named: "icon-map")
+ $0.setImage(imageView, for: .normal)
+ }
+
+ private let listIconView = UIButton().then {
+ let imageView = UIImage(named: "icon-list")
+ $0.setImage(imageView, for: .normal)
+ }
+
+ private let bottomLineView = UIView().then {
+ $0.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.05)
+ }
+
+ private let pageIconView = UIImageView().then {
+ $0.image = UIImage(named: "pageIcon")
+ }
+
+ private let bottombgView = UIView().then {
+ $0.backgroundColor = .black.withAlphaComponent(0.9)
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.navigationController?.navigationBar.isHidden = true
+
+ setBackgroundImage()
+ setConstraints()
+
+ setCollectionViewConfig()
+ setCollectionViewLayout()
+
+ setTableViewConfig()
+
+ let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapListIcon))
+ listIconView.addGestureRecognizer(tapGesture)
+ }
+
+ func setBackgroundImage() {
+ let backgroundImage = UIImageView().then {
+ $0.image = UIImage(named: "bg")
+ $0.contentMode = .scaleAspectFill
+ }
+
+ contentView.addSubview(backgroundImage)
+
+ backgroundImage.snp.makeConstraints {
+ $0.edges.equalToSuperview()
+ }
+ }
+
+ func setConstraints() {
+ scrollView.showsVerticalScrollIndicator = false
+ scrollView.contentInsetAdjustmentBehavior = .never
+ scrollView.alwaysBounceVertical = true
+
+ self.scrollView.backgroundColor = .black
+
+ self.view.addSubviews(scrollView,
+ bottombgView,
+ mapiconView,
+ listIconView,
+ bottomLineView,
+ pageIconView)
+
+ scrollView.addSubviews(contentView)
+
+ contentView.addSubviews(locationText,
+ degreeNumber,
+ statusText,
+ todayDegreeNumber,
+ descriptionView,
+ weeklyWeatherView)
+
+ descriptionView.addSubviews(todayWeatherScriptLabel,
+ lineView,
+ horizontalCollectionView)
+
+ weeklyWeatherView.addSubviews(calendarIcon,
+ weeklyWeatherSubTitle,
+ weeklyWeatherTableView)
+
+ contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
+ let contentViewHeight = contentView.heightAnchor.constraint(greaterThanOrEqualTo: view.heightAnchor)
+ contentViewHeight.priority = .defaultLow
+ contentViewHeight.isActive = true
+
+ scrollView.snp.makeConstraints {
+ $0.top.equalTo(self.view.snp.top)
+ $0.bottom.equalTo(self.view.snp.bottom)
+ $0.leading.trailing.equalToSuperview()
+ }
+
+ contentView.snp.makeConstraints {
+ $0.top.equalTo(scrollView.contentLayoutGuide.snp.top)
+ $0.bottom.equalTo(scrollView.contentLayoutGuide.snp.bottom)
+ $0.leading.equalTo(scrollView.contentLayoutGuide.snp.leading)
+ $0.trailing.equalTo(scrollView.contentLayoutGuide.snp.trailing)
+ }
+
+ locationText.snp.makeConstraints {
+ $0.centerX.equalToSuperview()
+ $0.top.equalToSuperview().inset(78)
+ }
+
+ degreeNumber.snp.makeConstraints {
+ $0.centerX.equalToSuperview()
+ $0.top.equalTo(locationText.snp.bottom).offset(4)
+ }
+
+ statusText.snp.makeConstraints {
+ $0.centerX.equalToSuperview()
+ $0.top.equalTo(degreeNumber.snp.bottom).offset(4)
+ }
+
+ todayDegreeNumber.snp.makeConstraints {
+ $0.centerX.equalToSuperview()
+ $0.top.equalTo(statusText.snp.bottom).offset(4)
+ }
+
+ descriptionView.snp.makeConstraints {
+ $0.top.equalTo(todayDegreeNumber.snp.bottom).offset(44)
+ $0.leading.equalToSuperview().inset(20)
+ $0.trailing.equalToSuperview().inset(20)
+ $0.height.equalTo(212)
+ }
+ todayWeatherScriptLabel.snp.makeConstraints {
+ $0.top.equalTo(descriptionView.snp.top).inset(10)
+ $0.leading.equalTo(descriptionView.snp.leading).inset(15)
+ $0.trailing.equalTo(descriptionView.snp.trailing).inset(15)
+ }
+ lineView.snp.makeConstraints {
+ $0.top.equalTo(todayWeatherScriptLabel.snp.bottom).offset(11)
+ $0.leading.equalTo(descriptionView.snp.leading).inset(14)
+ $0.trailing.equalTo(descriptionView.snp.trailing).inset(0)
+ $0.height.equalTo(0.2)
+ }
+ horizontalCollectionView.snp.makeConstraints {
+ $0.top.equalTo(lineView.snp.bottom)
+ $0.bottom.equalTo(descriptionView.snp.bottom)
+ $0.leading.equalTo(descriptionView.snp.leading).inset(20)
+ $0.trailing.equalTo(descriptionView.snp.trailing).inset(20)
+ }
+ weeklyWeatherView.snp.makeConstraints {
+ $0.top.equalTo(descriptionView.snp.bottom).offset(20)
+ $0.bottom.equalToSuperview().inset(86)
+ $0.height.equalTo(675)
+ $0.leading.equalToSuperview().inset(20)
+ $0.trailing.equalToSuperview().inset(20)
+ }
+ calendarIcon.snp.makeConstraints {
+ $0.top.equalToSuperview().inset(8)
+ $0.leading.equalToSuperview().inset(15)
+ }
+ weeklyWeatherSubTitle.snp.makeConstraints {
+ $0.top.equalTo(calendarIcon)
+ $0.leading.equalTo(calendarIcon.snp.trailing).offset(6)
+ }
+ weeklyWeatherTableView.snp.makeConstraints {
+ $0.top.equalTo(weeklyWeatherSubTitle.snp.bottom).offset(6)
+ $0.leading.trailing.bottom.equalToSuperview()
+ }
+ mapiconView.snp.makeConstraints {
+ $0.bottom.equalToSuperview().inset(34)
+ $0.leading.equalToSuperview().inset(10)
+ }
+ listIconView.snp.makeConstraints {
+ $0.bottom.equalToSuperview().inset(34)
+ $0.trailing.equalToSuperview().inset(10)
+ }
+ pageIconView.snp.makeConstraints {
+ $0.centerX.equalToSuperview()
+ $0.centerY.equalTo(listIconView)
+ $0.width.equalTo(52)
+ }
+ bottomLineView.snp.makeConstraints {
+ $0.bottom.equalTo(bottombgView.snp.top)
+ $0.leading.equalToSuperview()
+ $0.trailing.equalToSuperview()
+ $0.height.equalTo(1)
+ }
+ bottombgView.snp.makeConstraints {
+ $0.leading.equalToSuperview()
+ $0.trailing.equalToSuperview()
+ $0.height.equalTo(82)
+ $0.bottom.equalTo(0)
+ }
+ }
+
+ private func setCollectionViewConfig() {
+ self.horizontalCollectionView.register(TodayWeatherCollectionViewCell.self,
+ forCellWithReuseIdentifier: TodayWeatherCollectionViewCell.identifier)
+ self.horizontalCollectionView.delegate = self
+ self.horizontalCollectionView.dataSource = self
+ }
+
+ private func setCollectionViewLayout() {
+ let flowLayout = UICollectionViewFlowLayout()
+ flowLayout.itemSize = CGSize(width: 44 , height: 122)
+ flowLayout.scrollDirection = .horizontal
+ flowLayout.minimumLineSpacing = 22
+ flowLayout.minimumInteritemSpacing = 3
+ self.horizontalCollectionView.setCollectionViewLayout(flowLayout, animated: false)
+ }
+
+ private func setTableViewConfig() {
+ self.weeklyWeatherTableView.register(WeeklyWeatherTableViewCell.self,
+ forCellReuseIdentifier: WeeklyWeatherTableViewCell.identifier)
+ self.weeklyWeatherTableView.delegate = self
+ self.weeklyWeatherTableView.dataSource = self
+ }
+
+ @objc func tapListIcon() {
+ let listVC = ListViewController()
+ self.navigationController?.popViewController(animated: true)
+ }
+}
+
+extension DetailViewController: UICollectionViewDelegate {}
+
+extension DetailViewController: UICollectionViewDataSource {
+ func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+ return todayWeatherData.count
+ }
+
+ func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+ guard let item = collectionView.dequeueReusableCell(withReuseIdentifier: TodayWeatherCollectionViewCell.identifier,
+ for: indexPath) as? TodayWeatherCollectionViewCell else {return UICollectionViewCell()}
+ item.bindData(data: todayWeatherData[indexPath.row])
+ return item
+ }
+}
+
+extension DetailViewController: UITableViewDelegate {}
+
+extension DetailViewController: UITableViewDataSource {
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return weeklyWeatherData.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ guard let cell = tableView.dequeueReusableCell(withIdentifier: WeeklyWeatherTableViewCell.identifier,
+ for: indexPath) as? WeeklyWeatherTableViewCell else {return UITableViewCell()}
+
+ cell.bindData(data: weeklyWeatherData[indexPath.row])
+ cell.backgroundColor = .clear
+ cell.selectionStyle = .none
+ return cell
+ }
+
+}
diff --git a/Weather-App/Weather-App/Views/Detail/TodayWeatherCollectionViewCell.swift b/Weather-App/Weather-App/Views/Detail/TodayWeatherCollectionViewCell.swift
new file mode 100644
index 0000000..61d130c
--- /dev/null
+++ b/Weather-App/Weather-App/Views/Detail/TodayWeatherCollectionViewCell.swift
@@ -0,0 +1,67 @@
+//
+// TodayWeatherCollectionViewCell.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import UIKit
+import Then
+import SnapKit
+
+class TodayWeatherCollectionViewCell: UICollectionViewCell {
+
+ static let identifier: String = "TodayWeatherCollectionViewCell"
+
+ private let timeLabel = UILabel().then {
+ $0.font = UIFont(name: "SFProText-Medium", size: 17.0)
+ $0.textColor = .white
+ }
+
+ private let weatherIcon = UIImageView().then {
+ $0.contentMode = .scaleAspectFit
+ }
+
+ private let degree = UILabel().then {
+ $0.font = UIFont(name: "SFProText-Medium", size: 22.0)
+ $0.textColor = .white
+ }
+
+ override init(frame: CGRect) {
+ super.init(frame: frame)
+ setConstraints()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func setConstraints() {
+ self.addSubviews(timeLabel, weatherIcon, degree)
+
+ timeLabel.snp.makeConstraints {
+ $0.top.equalToSuperview().offset(4)
+ $0.centerX.equalToSuperview()
+ }
+
+ weatherIcon.snp.makeConstraints {
+ $0.top.equalTo(timeLabel.snp.bottom).offset(14)
+ $0.centerX.equalTo(timeLabel)
+ }
+
+ degree.snp.makeConstraints {
+ $0.bottom.equalToSuperview().offset(4)
+ $0.centerX.equalTo(timeLabel)
+ }
+
+ }
+
+ var itemRow: Int?
+
+ func bindData(data: TodayWeatherData) {
+ self.timeLabel.text = data.time
+ self.weatherIcon.image = UIImage(named: data.weatherImage)
+ self.degree.text = data.hourlyWeather
+ }
+
+}
diff --git a/Weather-App/Weather-App/Views/Detail/WeeklyWeatherTableViewCell.swift b/Weather-App/Weather-App/Views/Detail/WeeklyWeatherTableViewCell.swift
new file mode 100644
index 0000000..bfc4d22
--- /dev/null
+++ b/Weather-App/Weather-App/Views/Detail/WeeklyWeatherTableViewCell.swift
@@ -0,0 +1,93 @@
+//
+// WeeklyWeatherTableViewCell.swift
+// Weather-App
+//
+// Created by yeonsu on 11/9/23.
+//
+
+import UIKit
+import Then
+import SnapKit
+
+class WeeklyWeatherTableViewCell: UITableViewCell {
+
+ static let identifier: String = "WeeklyWeatherTableViewCell"
+
+ private let weekLabel = UILabel().then {
+ $0.font = UIFont(name: "SFProText-Medium", size: 22)
+ $0.textColor = .white
+ }
+
+ private let weatherIcon = UIImageView().then {
+ $0.contentMode = .scaleAspectFill
+ }
+
+ private let minTemperatureLabel = UILabel().then {
+ $0.font = UIFont(name: "SFProText-Medium", size: 22)
+ $0.textColor = .white
+ }
+
+ private let temperatureBarImage = UIImageView().then {
+ $0.contentMode = .scaleAspectFill
+ }
+
+ private let maxTemperatureLabel = UILabel().then {
+ $0.font = UIFont(name: "SFProText-Medium", size: 22)
+ $0.textColor = .white
+ }
+
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+ setConstraints()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func setConstraints() {
+ self.contentView.addSubviews(weekLabel,
+ weatherIcon,
+ minTemperatureLabel,
+ temperatureBarImage,
+ maxTemperatureLabel)
+
+ weekLabel.snp.makeConstraints {
+ $0.centerY.equalToSuperview()
+ $0.top.equalToSuperview().inset(14)
+ $0.leading.equalToSuperview().inset(16)
+ }
+ weatherIcon.snp.makeConstraints {
+ $0.centerY.equalToSuperview()
+ $0.top.equalToSuperview().inset(14)
+ $0.leading.equalTo(weekLabel.snp.trailing).offset(30)
+ $0.width.equalTo(32)
+ $0.height.equalTo(34)
+ }
+ minTemperatureLabel.snp.makeConstraints {
+ $0.centerY.equalToSuperview()
+ $0.top.equalToSuperview().inset(14)
+ $0.leading.equalTo(weatherIcon.snp.trailing).offset(15)
+ }
+ temperatureBarImage.snp.makeConstraints {
+ $0.centerY.equalToSuperview()
+ $0.leading.equalTo(minTemperatureLabel.snp.trailing).offset(6)
+ $0.trailing.equalTo(maxTemperatureLabel.snp.leading).offset(-6)
+ $0.width.equalTo(108)
+ }
+ maxTemperatureLabel.snp.makeConstraints {
+ $0.centerY.equalToSuperview()
+ $0.top.equalToSuperview().inset(14)
+ $0.leading.equalTo(temperatureBarImage.snp.trailing).inset(10)
+ $0.trailing.equalToSuperview().inset(17)
+ }
+ }
+
+ func bindData(data: WeeklyWeatherData) {
+ self.weekLabel.text = data.weekDay
+ self.weatherIcon.image = UIImage(named: data.weatherImage)
+ self.minTemperatureLabel.text = data.minimumTemperature
+ self.temperatureBarImage.image = UIImage(named: data.temperatureBarImage)
+ self.maxTemperatureLabel.text = data.maximumTemperature
+ }
+}
diff --git a/Weather-App/Weather-App/Views/List/ListTableViewCell.swift b/Weather-App/Weather-App/Views/List/ListTableViewCell.swift
new file mode 100644
index 0000000..ce50d0d
--- /dev/null
+++ b/Weather-App/Weather-App/Views/List/ListTableViewCell.swift
@@ -0,0 +1,138 @@
+//
+// ListTableViewCell.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import UIKit
+import Then
+import SnapKit
+import Combine
+
+class ListTableViewCell: UITableViewCell {
+
+ static let identifier: String = "ItemListTableViewCell"
+
+ private var getWeatherService: GetWeatherService = GetWeatherService()
+ private var cancellable: AnyCancellable?
+ public var cancellables: Set = []
+
+ override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+ super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+ // setPublishers()
+ setConstraints()
+
+ }
+
+ override func prepareForReuse() {
+ cancellable?.cancel()
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private let listView = UIImageView().then {
+ $0.image = UIImage(named: "bg-list")
+ }
+
+ private let myLocationLabel = UILabel().then {
+ let customTitleFont = UIFont(name: "SFProText-Bold", size: 24.0)
+ $0.font = customTitleFont
+ $0.text = ""
+ $0.textColor = .white
+ }
+
+ private let currentTimeLabel = UILabel().then {
+ let customTitleFont = UIFont(name: "SFProText-Medium", size: 17.0)
+ $0.font = customTitleFont
+ $0.text = ""
+ $0.textColor = .white
+ }
+
+ private let degreeNumber = UILabel().then {
+ let customDegreeFont = UIFont(name: "SFProText-Light", size: 52.0)
+ $0.font = customDegreeFont
+ $0.text = ""
+ $0.textColor = .white
+ }
+
+ private let statusText = UILabel().then {
+ let customStatusFont = UIFont(name: "SFProText-Regular", size: 16.0)
+ $0.font = customStatusFont
+ $0.text = ""
+ $0.textColor = .white
+ }
+
+ private let todayDegreeNumber = UILabel().then {
+ let customTodayDegreeFont = UIFont(name: "SFProText-Regular", size: 16.0)
+ $0.font = customTodayDegreeFont
+ $0.text = ""
+ $0.textColor = .white
+ }
+
+ private func setConstraints() {
+
+ self.contentView.addSubview(listView)
+ self.listView.addSubviews(myLocationLabel,
+ currentTimeLabel,
+ degreeNumber,
+ statusText,
+ todayDegreeNumber
+ )
+ self.contentView.backgroundColor = .black
+
+ listView.snp.makeConstraints {
+ $0.leading.equalToSuperview()
+ $0.trailing.equalToSuperview()
+ $0.height.equalTo(117)
+ }
+ myLocationLabel.snp.makeConstraints {
+ $0.leading.equalToSuperview().inset(16)
+ $0.top.equalToSuperview().inset(10)
+ }
+ currentTimeLabel.snp.makeConstraints {
+ $0.leading.equalToSuperview().inset(16)
+ $0.top.equalTo(myLocationLabel.snp.bottom).offset(2)
+ }
+ degreeNumber.snp.makeConstraints {
+ $0.top.equalToSuperview()
+ $0.trailing.equalToSuperview().inset(16)
+ }
+ statusText.snp.makeConstraints {
+ $0.leading.equalTo(currentTimeLabel)
+ $0.bottom.equalToSuperview().inset(10)
+ }
+ todayDegreeNumber.snp.makeConstraints {
+ $0.trailing.equalTo(degreeNumber)
+ $0.bottom.equalTo(statusText)
+ }
+ }
+
+ func setUI(weather: WeatherResponse) {
+ myLocationLabel.text = weather.name
+ currentTimeLabel.text = convertTimeZoneOffsetToString(weather.timezone)
+ degreeNumber.text = "\(formatNumberToOneDecimalPlace(weather.main.temp!))°"
+ todayDegreeNumber.text = "최고:\(formatNumberToOneDecimalPlace(weather.main.temp_max!))° 최저:\(formatNumberToOneDecimalPlace(weather.main.temp_min!))°"
+ }
+
+ func convertTimeZoneOffsetToString(_ offset: Int) -> String {
+ let hours = offset / 3600
+ let minutes = abs((offset % 3600) / 60)
+
+ let formatter = DateFormatter()
+ formatter.dateFormat = "HH:mm"
+ formatter.timeZone = TimeZone(secondsFromGMT: offset)
+
+ let formattedString = formatter.string(from: Date())
+
+ return formattedString
+ }
+
+ func formatNumberToOneDecimalPlace(_ number: Double) -> String {
+ let formattedNumber = String(format: "%.1f", number)
+ return formattedNumber
+ }
+}
diff --git a/Weather-App/Weather-App/Views/List/ListViewController.swift b/Weather-App/Weather-App/Views/List/ListViewController.swift
new file mode 100644
index 0000000..e845a17
--- /dev/null
+++ b/Weather-App/Weather-App/Views/List/ListViewController.swift
@@ -0,0 +1,159 @@
+//
+// ListViewController.swift
+// Weather-App
+//
+// Created by yeonsu on 11/8/23.
+//
+
+import UIKit
+import Then
+import SnapKit
+import Combine
+
+class ListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
+
+ private var getWeatherService: GetWeatherService = GetWeatherService()
+ private var cancellable: AnyCancellable?
+
+ private let cities: [String] = ["seoul", "gwangju", "gumi", "gunsan", "daegu"]
+ private var weatherResponse: [WeatherResponse] = []
+
+ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+ return weatherResponse.count
+ }
+
+ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ guard let cell = tableView.dequeueReusableCell(withIdentifier: ListTableViewCell.identifier, for: indexPath) as? ListTableViewCell else {
+ return UITableViewCell()
+ }
+
+ var city = cities[indexPath.row]
+ cell.setUI(weather: weatherResponse[indexPath.row])
+
+ return cell
+ }
+
+ private let etcButton = UIButton().then {
+ let etcIcon = UIImage(named: "icon-etc")
+ $0.setImage(etcIcon, for: .normal)
+ }
+
+ private let tableView = UITableView(frame: .zero, style: .plain).then {
+ $0.backgroundColor = .init(.black)
+ $0.separatorStyle = .none
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ self.navigationController?.navigationBar.isHidden = true
+
+ self.view.addSubviews(etcButton,
+ tableView)
+
+ setPublishers()
+ setConstraints()
+ setTableViewConfig()
+ self.tableView.alwaysBounceVertical = true
+
+ }
+
+ private func setTableViewConfig() {
+ self.tableView.register(ListTableViewCell.self,
+ forCellReuseIdentifier: ListTableViewCell.identifier)
+ self.tableView.delegate = self
+ self.tableView.dataSource = self
+ }
+
+ private func setConstraints() {
+ etcButton.snp.makeConstraints {
+ $0.top.equalTo(self.view.safeAreaLayoutGuide.snp.top)
+ $0.trailing.equalToSuperview().inset(20)
+ }
+
+ tableView.snp.makeConstraints {
+ $0.top.equalTo(etcButton.snp.bottom)
+ $0.leading.equalToSuperview().inset(20)
+ $0.trailing.equalToSuperview().inset(20)
+ $0.bottom.equalToSuperview()
+ }
+ }
+
+ public let searchTextField = UITextField().then {
+ let placeholderTextColor = UIColor.lightGray
+ $0.borderStyle = .roundedRect
+ $0.placeholder = "도시 또는 공항 검색"
+ $0.textColor = .white
+
+ let attributes: [NSAttributedString.Key: Any] = [
+ NSAttributedString.Key.foregroundColor: placeholderTextColor
+ ]
+ $0.attributedPlaceholder = NSAttributedString(string: $0.placeholder ?? "", attributes: attributes)
+ $0.backgroundColor = .secondarySystemFill
+
+ let imageView = UIImageView()
+ imageView.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
+ imageView.image = UIImage(named: "icon-search")
+ $0.leftView = imageView
+ $0.leftViewMode = .always
+
+ let marginView = UIView()
+ imageView.frame = CGRect(x: 0, y: 0, width: 8, height: 24)
+ }
+
+ func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+ let headerView = UIView()
+
+ let appTitle = UILabel().then {
+ let customFont = UIFont(name: "SFProText-Bold", size: 36.0)
+ $0.font = customFont
+ $0.text = "날씨"
+ $0.textColor = .white
+ }
+
+ headerView.addSubviews(appTitle, searchTextField)
+ appTitle.snp.makeConstraints {
+ $0.top.equalToSuperview()
+ $0.leading.equalToSuperview()
+ }
+ searchTextField.snp.makeConstraints {
+ $0.top.equalTo(appTitle.snp.bottom).offset(8)
+ $0.leading.equalToSuperview()
+ $0.trailing.equalToSuperview()
+ $0.size.equalTo(44)
+ }
+ return headerView
+ }
+
+ func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+ return 108.0
+ }
+
+ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+ return 132.0
+ }
+
+
+ @objc func tapMyLocationView() {
+ let detailVC = DetailViewController()
+ self.navigationController?.pushViewController(detailVC, animated: true)
+ }
+
+ private func setPublishers() {
+ let publisher = Publishers.Sequence<[String], Never>(sequence: cities)
+
+ self.cancellable = publisher
+ .flatMap { [weak self] city in
+ guard let self = self else {return Just(WeatherResponse(main: Main.placeholder, timezone: 0, name: "")).eraseToAnyPublisher() } // weak self로 만들어주는 publisher
+ return self.getWeatherService.fetchWeather(city: city)
+ .catch { error in
+ Just(WeatherResponse(main: Main.placeholder, timezone: 0, name: ""))
+ }
+ .eraseToAnyPublisher()
+ }
+ .collect() // 5개 데이터 -> 배열로!
+ .sink { [weak self] weather in
+ self?.weatherResponse = weather
+ self?.tableView.reloadData()
+ }
+ }
+}