diff --git a/sopt_fourth_seminar0/AppDelegate.swift b/sopt_fourth_seminar0/AppDelegate.swift new file mode 100644 index 0000000..cca5884 --- /dev/null +++ b/sopt_fourth_seminar0/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// sopt_fourth_seminar0 +// +// Created by Woo Jye Lee on 11/15/23. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/sopt_fourth_seminar0/Assets.xcassets/AccentColor.colorset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/AppIcon.appiconset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..f384105 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "pngegg (3).png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/AppIcon.appiconset/pngegg (3).png b/sopt_fourth_seminar0/Assets.xcassets/AppIcon.appiconset/pngegg (3).png new file mode 100644 index 0000000..a92596a Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/AppIcon.appiconset/pngegg (3).png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/background_color.colorset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/background_color.colorset/Contents.json new file mode 100644 index 0000000..1ec4cb3 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/background_color.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x40", + "green" : "0x30", + "red" : "0x2A" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x40", + "green" : "0x30", + "red" : "0x2A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Clear.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/Clear.imageset/Contents.json new file mode 100644 index 0000000..ae549d1 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Clear.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 6.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Clear.imageset/Frame 6.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/Clear.imageset/Frame 6.svg new file mode 100644 index 0000000..ac0115e --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Clear.imageset/Frame 6.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Clouds.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/Clouds.imageset/Contents.json new file mode 100644 index 0000000..04464da --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Clouds.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 2.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Clouds.imageset/Frame 2.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/Clouds.imageset/Frame 2.svg new file mode 100644 index 0000000..8d4c7ee --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Clouds.imageset/Frame 2.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Rain.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/Rain.imageset/Contents.json new file mode 100644 index 0000000..b40d9a4 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Rain.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 3.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Rain.imageset/Frame 3.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/Rain.imageset/Frame 3.svg new file mode 100644 index 0000000..367669f --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Rain.imageset/Frame 3.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Snow.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/Snow.imageset/Contents.json new file mode 100644 index 0000000..76ca6a8 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Snow.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 4.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/Snow.imageset/Frame 4.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/Snow.imageset/Frame 4.svg new file mode 100644 index 0000000..d77743f --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/Snow.imageset/Frame 4.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/arrow.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/arrow.imageset/Contents.json new file mode 100644 index 0000000..ad8b1af --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/arrow.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 10.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/arrow.imageset/Frame 10.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/arrow.imageset/Frame 10.svg new file mode 100644 index 0000000..58442bf --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/arrow.imageset/Frame 10.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/dot.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/dot.imageset/Contents.json new file mode 100644 index 0000000..a23c57f --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/dot.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 11.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/dot.imageset/Frame 11.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/dot.imageset/Frame 11.svg new file mode 100644 index 0000000..d69254f --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/dot.imageset/Frame 11.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/dots.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/dots.imageset/Contents.json new file mode 100644 index 0000000..32f108d --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/dots.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 7.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/dots.imageset/Frame 7.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/dots.imageset/Frame 7.svg new file mode 100644 index 0000000..7e2ceeb --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/dots.imageset/Frame 7.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/listbar.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/listbar.imageset/Contents.json new file mode 100644 index 0000000..8b0f1c7 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/listbar.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 12.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/listbar.imageset/Frame 12.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/listbar.imageset/Frame 12.svg new file mode 100644 index 0000000..b0a0fa1 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/listbar.imageset/Frame 12.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/map.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/map.imageset/Contents.json new file mode 100644 index 0000000..bde26d0 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/map.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 9.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/map.imageset/Frame 9.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/map.imageset/Frame 9.svg new file mode 100644 index 0000000..3adedf3 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/map.imageset/Frame 9.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/search.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/search.imageset/Contents.json new file mode 100644 index 0000000..d84ab34 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/search.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 8.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/search.imageset/Frame 8.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/search.imageset/Frame 8.svg new file mode 100644 index 0000000..b100ed1 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/search.imageset/Frame 8.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/thunder.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/icons/thunder.imageset/Contents.json new file mode 100644 index 0000000..e00c5f5 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/thunder.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Frame 5.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/icons/thunder.imageset/Frame 5.svg b/sopt_fourth_seminar0/Assets.xcassets/icons/thunder.imageset/Frame 5.svg new file mode 100644 index 0000000..5cb0d68 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/icons/thunder.imageset/Frame 5.svg @@ -0,0 +1,3 @@ + + + diff --git a/sopt_fourth_seminar0/Assets.xcassets/img/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/img/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/img/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/img/large_background.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/img/large_background.imageset/Contents.json new file mode 100644 index 0000000..17f6fd9 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/img/large_background.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Image.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/img/large_background.imageset/Image.png b/sopt_fourth_seminar0/Assets.xcassets/img/large_background.imageset/Image.png new file mode 100644 index 0000000..48e4bdf Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/img/large_background.imageset/Image.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/img/small_background.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/img/small_background.imageset/Contents.json new file mode 100644 index 0000000..17f6fd9 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/img/small_background.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Image.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/img/small_background.imageset/Image.png b/sopt_fourth_seminar0/Assets.xcassets/img/small_background.imageset/Image.png new file mode 100644 index 0000000..03af1ce Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/img/small_background.imageset/Image.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar1.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar1.imageset/Contents.json new file mode 100644 index 0000000..4166d18 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 001png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar1.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 001png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar1.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 001png.png new file mode 100644 index 0000000..e206b70 Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar1.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 001png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar10.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar10.imageset/Contents.json new file mode 100644 index 0000000..1a2f22c --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar10.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 010png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar10.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 010png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar10.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 010png.png new file mode 100644 index 0000000..6191d7f Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar10.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 010png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar2.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar2.imageset/Contents.json new file mode 100644 index 0000000..3bdf2d9 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 002png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar2.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 002png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar2.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 002png.png new file mode 100644 index 0000000..611ce77 Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar2.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 002png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar3.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar3.imageset/Contents.json new file mode 100644 index 0000000..dd6b1ed --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 003png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar3.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 003png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar3.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 003png.png new file mode 100644 index 0000000..af1384c Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar3.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 003png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar4.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar4.imageset/Contents.json new file mode 100644 index 0000000..ba31004 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar4.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 004png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar4.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 004png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar4.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 004png.png new file mode 100644 index 0000000..16c9a04 Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar4.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 004png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar5.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar5.imageset/Contents.json new file mode 100644 index 0000000..5988538 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar5.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 005png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar5.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 005png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar5.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 005png.png new file mode 100644 index 0000000..af1384c Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar5.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 005png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar6.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar6.imageset/Contents.json new file mode 100644 index 0000000..1a85cc7 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar6.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 006png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar6.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 006png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar6.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 006png.png new file mode 100644 index 0000000..e901e7d Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar6.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 006png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar7.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar7.imageset/Contents.json new file mode 100644 index 0000000..f0c9431 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar7.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 007png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar7.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 007png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar7.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 007png.png new file mode 100644 index 0000000..6191d7f Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar7.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 007png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar8.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar8.imageset/Contents.json new file mode 100644 index 0000000..6ac352e --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar8.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 008png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar8.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 008png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar8.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 008png.png new file mode 100644 index 0000000..6191d7f Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar8.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 008png.png differ diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar9.imageset/Contents.json b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar9.imageset/Contents.json new file mode 100644 index 0000000..b06c6f4 --- /dev/null +++ b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar9.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "KakaoTalk_Photo_2023-11-09-16-19-33 009png.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar9.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 009png.png b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar9.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 009png.png new file mode 100644 index 0000000..3cd5dc8 Binary files /dev/null and b/sopt_fourth_seminar0/Assets.xcassets/weatherbar/bar9.imageset/KakaoTalk_Photo_2023-11-09-16-19-33 009png.png differ diff --git a/sopt_fourth_seminar0/Base.lproj/LaunchScreen.storyboard b/sopt_fourth_seminar0/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/sopt_fourth_seminar0/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sopt_fourth_seminar0/Client/ImageCollectionViewCell.swift b/sopt_fourth_seminar0/Client/ImageCollectionViewCell.swift new file mode 100644 index 0000000..5a6f172 --- /dev/null +++ b/sopt_fourth_seminar0/Client/ImageCollectionViewCell.swift @@ -0,0 +1,62 @@ +import UIKit +import SnapKit +import Then + +class ImageCollectionViewCell: UICollectionViewCell { + + static let identifier: String = "ImageCollectionViewCell" + + override init(frame: CGRect) { + super.init(frame: frame) + self.setLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setLayout() { + [image, temp, time].forEach() { + // collection view 안에는 기본적으로 콘텐츠 뷰가 들어있나? + self.contentView.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + + // problem...** + NSLayoutConstraint.activate([image.centerXAnchor.constraint(equalTo: contentView.centerXAnchor), + image.centerYAnchor.constraint(equalTo: contentView.centerYAnchor) + ]) + NSLayoutConstraint.activate([ + time.bottomAnchor.constraint(equalTo: centerYAnchor, constant: -40), + time.centerXAnchor.constraint(equalTo: centerXAnchor) + ]) + NSLayoutConstraint.activate([ + temp.topAnchor.constraint(equalTo: centerYAnchor, constant: 40), + temp.centerXAnchor.constraint(equalTo: centerXAnchor) + ]) + + } + + func bindData(data: ImageCollectionData) { + self.image.image = UIImage(named: data.weather) + self.temp.text = "\(data.temperature)°C" + self.time.text = "\(data.time)" + } + let image: UIImageView = { + let weatherimg = UIImageView() + weatherimg.contentMode = .scaleAspectFit + return weatherimg + }() + let temp: UILabel = { + let view = UILabel() + view.textColor = .white + view.font = UIFont(name: "SFProDisplay-Medium", size: 19) + return view + }() + let time: UILabel = { + let view = UILabel() + view.textColor = .white + view.font = UIFont(name: "SFProDisplay-Medium", size: 19) + return view + }() +} diff --git a/sopt_fourth_seminar0/Client/ItemListTableViewCell.swift b/sopt_fourth_seminar0/Client/ItemListTableViewCell.swift new file mode 100644 index 0000000..dd4cd74 --- /dev/null +++ b/sopt_fourth_seminar0/Client/ItemListTableViewCell.swift @@ -0,0 +1,115 @@ +import UIKit +import SnapKit +import Then + +class ItemListTableViewCell: UITableViewCell { + + static let identifier: String = "ItemListTableViewCell" + weak var delegate: WeatherInfoViewDelegate? + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + self.setLayout() + self.setupGestureRecognizers() + self.layer.cornerRadius = 20 + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func bindData(data: ItemListData) { + self.locationLabel.text = data.location + self.maxtemp.text = "최고: \(data.maxtemp)°C" + self.mintemp.text = "최저: \(data.mintemp)°C" + self.tempLabel.text = "\(data.temperature)°C" + self.weatherLabel.text = data.weather + } + + // tableviewcell 내에 UItableviewcell contentview가 있음 + override func layoutSubviews() { + super.layoutSubviews() + contentView.frame = contentView.frame.inset(by: UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)) + } + + private func setLayout() { + self.backgroundColor = .black + [backgroundimage, locationLabel, weatherLabel, tempLabel, maxtemp, mintemp].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview($0) + } + backgroundimage.snp.makeConstraints { + $0.edges.equalTo(contentView) + } + + NSLayoutConstraint.activate([ + locationLabel.topAnchor.constraint(equalTo: backgroundimage.topAnchor, constant: 7), + locationLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15) + ]) + + NSLayoutConstraint.activate([ + weatherLabel.bottomAnchor.constraint(equalTo: backgroundimage.bottomAnchor, constant: -7), + weatherLabel.leadingAnchor.constraint(equalTo: locationLabel + .leadingAnchor) + ]) + + NSLayoutConstraint.activate([ + tempLabel.topAnchor.constraint(equalTo: locationLabel.topAnchor), + tempLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15) + ]) + + NSLayoutConstraint.activate([ + mintemp.bottomAnchor.constraint(equalTo: weatherLabel.bottomAnchor), + mintemp.trailingAnchor.constraint(equalTo: tempLabel.trailingAnchor) + ]) + + NSLayoutConstraint.activate([ + maxtemp.bottomAnchor.constraint(equalTo: weatherLabel.bottomAnchor), + maxtemp.trailingAnchor.constraint(equalTo: mintemp.leadingAnchor, constant: -5) + ]) + } + + let backgroundimage: UIImageView = { + let imageView = UIImageView(image: UIImage(named: "small_background")) + imageView.contentMode = .scaleAspectFill + return imageView + }() + + let locationLabel = UILabel().then { + $0.font = UIFont(name: "SFProDisplay-Medium", size: 24) + $0.textColor = .white + } + + let maxtemp = UILabel().then { + $0.font = UIFont(name: "SFProDisplay-Medium", size: 15) + $0.textColor = .white + } + + let mintemp = UILabel().then { + $0.font = UIFont(name: "SFProDisplay-Medium", size: 15) + $0.textColor = .white + } + + let weatherLabel = UILabel().then { + $0.font = UIFont(name: "SFProDisplay-Medium", size: 16) + $0.textColor = .white + } + + let tempLabel = UILabel().then { + $0.font = UIFont(name: "SFProDisplay-Light", size: 52) + $0.textColor = .white + } + + // tap 감지는 weatherinfo 객체가 하고, 감지에 대한 이벤트 처리는 delegate로 + private func setupGestureRecognizers() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) + self.addGestureRecognizer(tapGesture) + } + + // cell click에 대한 화면전환은 viewcontroller가 수행 + @objc private func handleTap(_ sender: UITapGestureRecognizer) { + delegate?.weatherInfoViewTapped(self) + } + +} + diff --git a/sopt_fourth_seminar0/Client/SecondViewController.swift b/sopt_fourth_seminar0/Client/SecondViewController.swift new file mode 100644 index 0000000..6c1b82e --- /dev/null +++ b/sopt_fourth_seminar0/Client/SecondViewController.swift @@ -0,0 +1,425 @@ +import UIKit +import SnapKit +import Then + +class SecondViewController: UIViewController { + var Location: String + var Temperature: String + var Maxtemp: String + var Mintemp: String + var Weather: String + + override func viewDidLoad() { + super.viewDidLoad() + self.navigationItem.title = "SecondViewController" + self.setCollectionViewConfig() + self.setCollectionViewLayout() + self.setTableViewConfig() + self.setLayout() + self.setBackgroundImage() + self.tableView.backgroundColor = .clear + Task { + await fetchWeatherInfo() + } + } + + init(location: String, temperature: String, weather: String, maxtemp: String, mintemp: String) { + self.Location = location + self.Temperature = temperature + self.Maxtemp = maxtemp + self.Mintemp = mintemp + self.Weather = weather + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func fetchWeatherInfo() async { + let city = self.Location + imageCollectionList = [] // append를 쓰기 때문에 배열 초기화해줘야함 + + for i in 0...11 { + do { + guard let currentWeather = try await GetTimeline.shared.GetTimelineData(cityname: city) else {return} + let weatherInfo = ImageCollectionData(time: currentWeather.list[i].dtTxt, weather: currentWeather.list[i].weather[0].main.rawValue, temperature: currentWeather.list[i].main.temp) + + imageCollectionList.append(weatherInfo) + // searchWeatherInfoListData = itemListData + } catch { + print(error) + } + } + + do { + guard let currentWeather = try await GetTimeline.shared.GetTimelineData(cityname: city) else {return} + guard let time = extractHour(from: currentWeather.list[0].dtTxt) else {return} + self.descriptionView.text = "\(String(describing: time))에 \(self.Weather) 상태가 예상됩니다." + } catch { + print(error) + } + collectionview.reloadData() + } + + func extractHour(from dateString: String) -> String? { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + + if let date = dateFormatter.date(from: dateString) { + let calendar = Calendar.current + let hour = calendar.component(.hour, from: date) + return String(format: "%02d:00~%02d:00", hour, hour+3) + } + + return nil + } + + private func setLayout() { + [bottomview, scrollview].forEach() { + self.view.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + scrollview.addSubview(contentview2) + contentview2.translatesAutoresizingMaskIntoConstraints = false + scrollview.addSubview(topview) + + scrollview.snp.makeConstraints { + $0.top.leading.trailing.equalToSuperview() + $0.bottom.equalToSuperview().inset(80) + } + // problem..** + NSLayoutConstraint.activate([contentview2.topAnchor.constraint(equalTo: scrollview.contentLayoutGuide.topAnchor), + contentview2.bottomAnchor.constraint(equalTo: scrollview.contentLayoutGuide.bottomAnchor), + contentview2.heightAnchor.constraint(equalToConstant: 1070), + contentview2.widthAnchor.constraint(equalTo: scrollview.widthAnchor), + contentview2.leadingAnchor.constraint(equalTo: scrollview.contentLayoutGuide.leadingAnchor), + contentview2.trailingAnchor.constraint(equalTo: scrollview.contentLayoutGuide.trailingAnchor) + ]) + + [box, box2, topview].forEach() { + contentview2.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + [contentview, lineView, descriptionView].forEach() { + box.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + contentview.addSubview(collectionview) + collectionview.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([topview.topAnchor.constraint(equalTo: contentview2.topAnchor, constant: 0), + topview.leadingAnchor.constraint(equalTo: contentview2.leadingAnchor, constant: 0), + topview.trailingAnchor.constraint(equalTo: contentview2.trailingAnchor, constant: 0), + topview.heightAnchor.constraint(equalToConstant: 300) + ]) + NSLayoutConstraint.activate([bottomview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0), + bottomview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0), + bottomview.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0), + bottomview.heightAnchor.constraint(equalToConstant: 82) + ]) + NSLayoutConstraint.activate([ + box.topAnchor.constraint(equalTo: topview.bottomAnchor, constant: 20), + box.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 17), + box.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -17), + box.heightAnchor.constraint(equalToConstant: 212) + ]) + NSLayoutConstraint.activate([descriptionView.topAnchor.constraint(equalTo: box.topAnchor, constant: 8), + descriptionView.leadingAnchor.constraint(equalTo: box.leadingAnchor, constant: 13), + descriptionView.trailingAnchor.constraint(equalTo: box.trailingAnchor, constant: -13) + ]) + NSLayoutConstraint.activate([lineView.topAnchor.constraint(equalTo: descriptionView.bottomAnchor, constant: 10), + lineView.heightAnchor.constraint(equalToConstant: 0.3), + lineView.leadingAnchor.constraint(equalTo: box.leadingAnchor, constant: 10), + lineView.trailingAnchor.constraint(equalTo: box.trailingAnchor, constant: -10) + ]) + NSLayoutConstraint.activate([contentview.topAnchor.constraint(equalTo: lineView.bottomAnchor), + contentview.leadingAnchor.constraint(equalTo: box.leadingAnchor), + contentview.trailingAnchor.constraint(equalTo: box.trailingAnchor), + contentview.bottomAnchor.constraint(equalTo: box.bottomAnchor) + ]) + NSLayoutConstraint.activate([collectionview.topAnchor.constraint(equalTo: contentview.topAnchor), + collectionview.leadingAnchor.constraint(equalTo: contentview.leadingAnchor), + collectionview.trailingAnchor.constraint(equalTo: contentview.trailingAnchor), + collectionview.bottomAnchor.constraint(equalTo: contentview.bottomAnchor) + ]) + + bottomview.addSubview(listbar) + listbar.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([listbar.centerYAnchor.constraint(equalTo: bottomview.centerYAnchor), + listbar.trailingAnchor.constraint(equalTo: bottomview.trailingAnchor, constant: -30) + ]) + listbar.addTarget(self, action: #selector(popDetailVC(_:)), for: .touchUpInside) + + let line2 = UIView() + line2.backgroundColor = UIColor.white.withAlphaComponent(0.3) + let label2 = UILabel() + label2.textColor = .lightGray + label2.font = UIFont(name: "SFProDisplay-Regular", size: 15) + label2.text = "10일간의 일기예보" + let icon = UIImageView() + icon.image = UIImage(systemName: "calendar") + icon.contentMode = .scaleAspectFit + icon.tintColor = .lightGray + + [label2, icon, line2, tableView].forEach() { + box2.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + + NSLayoutConstraint.activate([box2.topAnchor.constraint(equalTo: box.bottomAnchor, constant: 20), + box2.leadingAnchor.constraint(equalTo: box.leadingAnchor), + box2.trailingAnchor.constraint(equalTo: box.trailingAnchor), + box2.heightAnchor.constraint(equalToConstant: 650) + ]) + NSLayoutConstraint.activate([icon.leadingAnchor.constraint(equalTo: box2.leadingAnchor, constant: 10), + icon.centerYAnchor.constraint(equalTo: label2.centerYAnchor) + ]) + NSLayoutConstraint.activate([label2.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: 10), + label2.topAnchor.constraint(equalTo: box2.topAnchor, constant: 5) + ]) + NSLayoutConstraint.activate([line2.topAnchor.constraint(equalTo: label2.bottomAnchor, constant: 5), + line2.leadingAnchor.constraint(equalTo: box2.leadingAnchor, constant: 10), + line2.trailingAnchor.constraint(equalTo: box2.trailingAnchor, constant: -10), + line2.heightAnchor.constraint(equalToConstant: 0.3) + ]) + NSLayoutConstraint.activate([tableView.topAnchor.constraint(equalTo: line2.bottomAnchor), + tableView.leadingAnchor.constraint(equalTo: box2.leadingAnchor), + tableView.trailingAnchor.constraint(equalTo: box2.trailingAnchor), + tableView.bottomAnchor.constraint(equalTo: box2.bottomAnchor), + tableView.heightAnchor.constraint(equalToConstant: 590) + ]) + + // 하단바 앞으로 가져오기 + self.view.bringSubviewToFront(bottomview) + } + + // collection view 설정 + private func setCollectionViewConfig() { + self.collectionview.register(ImageCollectionViewCell.self, + forCellWithReuseIdentifier: ImageCollectionViewCell.identifier) + self.collectionview.delegate = self + self.collectionview.dataSource = self + } + // 1x3 레이아웃은 어디서 설정해 주어야 하는지? + private func setCollectionViewLayout() { + let flowLayout = UICollectionViewFlowLayout() + flowLayout.scrollDirection = .horizontal + flowLayout.itemSize = CGSize(width: (self.box.bounds.width) / 5 , height: self.contentview.bounds.height) // cell 크기 설정 + flowLayout.minimumLineSpacing = 0 + flowLayout.minimumInteritemSpacing = 0 + self.collectionview.setCollectionViewLayout(flowLayout, animated: false) + self.collectionview.isOpaque = false // 배경 불투명하게 처리 + self.collectionview.backgroundColor = UIColor.clear // 해당뷰 배경제거 + } + + // 수직스크롤뷰 + var scrollview: UIScrollView = { + let view = UIScrollView() + view.alwaysBounceVertical = true + view.showsVerticalScrollIndicator = false + return view + }() + + // topview 설정 + lazy var topview: UIView = { + let view = UIView() + + let location = UILabel() + location.textColor = .white + location.text = self.Location + location.font = UIFont(name: "SFProDisplay-Regular", size: 36) + + let max = UILabel() + max.textColor = .white + max.text = self.Maxtemp + max.font = UIFont(name: "SFProDisplay-Regular", size: 20) + + let min = UILabel() + min.textColor = .white + min.text = self.Mintemp + min.font = UIFont(name: "SFProDisplay-Regular", size: 20) + + let weather = UILabel() + weather.textColor = .white + weather.text = self.Weather + weather.font = UIFont(name: "SFProDisplay-Regular", size: 24) + + let temperature = UILabel() + temperature.textColor = .white + temperature.text = self.Temperature + temperature.font = UIFont(name: "SFProDisplay-Light", size: 102) + + [location, temperature, weather, max, min].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + view.addSubview($0) + } + NSLayoutConstraint.activate([location.topAnchor.constraint(equalTo: view.topAnchor, constant: 50), + location.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + NSLayoutConstraint.activate([temperature.topAnchor.constraint(equalTo: location.bottomAnchor, constant: 10), + temperature.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + NSLayoutConstraint.activate([weather.topAnchor.constraint(equalTo: temperature.bottomAnchor, constant: 10), + weather.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + NSLayoutConstraint.activate([max.topAnchor.constraint(equalTo: weather.bottomAnchor, constant: 10), + max.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -3) + ]) + NSLayoutConstraint.activate([min.topAnchor.constraint(equalTo: weather.bottomAnchor, constant: 10), + min.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 3) + ]) + return view + }() + + let contentview = UIView() + let contentview2 = UIView() + + var box: UIView = { + let view = UIView() + view.backgroundColor = UIColor.black.withAlphaComponent(0.2) + view.layer.cornerRadius = 20 + view.layer.borderWidth = 0.3 + view.layer.borderColor = UIColor.lightGray.cgColor + return view + }() + + lazy var descriptionView: UILabel = { + let label = UILabel() + // label.text = "08:00~09:00에 강우 상태가, 18:00에 한때 흐린 상태가 예상됩니다." + label.numberOfLines = 0 + label.textColor = .white + label.font = UIFont(name: "SFProDisplay-Regular", size: 17) + + return label + }() + + private let lineView: UIView = { + let view = UIView() + view.backgroundColor = UIColor.white.withAlphaComponent(0.3) + return view + }() + + // 하단바 설정 + lazy var bottomview: UIView = { + let view = UIView() + view.backgroundColor = UIColor(named: "background_color") + let line = UIView() + line.backgroundColor = .white + let mapbutton = UIButton() + mapbutton.setImage(UIImage(named: "map"), for: .normal) + let arrow = UIButton() + arrow.setImage(UIImage(named: "arrow"), for: .normal) + let dot = UIButton() + dot.setImage(UIImage(named: "dot"), for: .normal) + + [mapbutton, arrow, dot, line].forEach { + $0.translatesAutoresizingMaskIntoConstraints = false + view.addSubview($0) + } + NSLayoutConstraint.activate([line.topAnchor.constraint(equalTo: view.topAnchor), + line.leadingAnchor.constraint(equalTo: view.leadingAnchor), + line.trailingAnchor.constraint(equalTo: view.trailingAnchor), + line.heightAnchor.constraint(equalToConstant: 0.3) + ]) + NSLayoutConstraint.activate([mapbutton.centerYAnchor.constraint(equalTo: view.centerYAnchor), + mapbutton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30) + ]) + NSLayoutConstraint.activate([arrow.centerYAnchor.constraint(equalTo: view.centerYAnchor), + arrow.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -10) + ]) + NSLayoutConstraint.activate([dot.centerYAnchor.constraint(equalTo: view.centerYAnchor), + dot.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 10) + ]) + return view + }() + + // backbutton + var listbar: UIButton = { + let button = UIButton() + button.setImage(UIImage(named: "listbar"), for: .normal) + return button + }() + + private let collectionview = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + private let collectionview2 = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + + // box 객체를 두번이상 선언하지 않고 한번에 해결하는법은..? -> compositional layout + var box2: UIView = { + let view = UIView() + view.backgroundColor = UIColor.black.withAlphaComponent(0.2) + view.layer.cornerRadius = 20 + view.layer.borderWidth = 0.3 + view.layer.borderColor = UIColor.lightGray.cgColor + return view + }() + + // UIView 대신 콜렉션뷰 헤더로 라벨 채울수있을까..? + private func setBackgroundImage() { + let backgroundImageView = UIImageView(image: UIImage(named: "large_background")) + backgroundImageView.contentMode = .scaleAspectFill + backgroundImageView.frame = view.bounds + self.view.insertSubview(backgroundImageView, at: 0) + } + + @objc + func popDetailVC(_ sender: UIButton) { + self.navigationController?.popViewController(animated: true) + } + + private func setTableViewConfig() { + self.tableView.register(WeatherTableViewCell.self, + forCellReuseIdentifier: WeatherTableViewCell.identifier) + self.tableView.delegate = self + self.tableView.dataSource = self + self.tableView.separatorStyle = .singleLine + } + // problem..** + private let tableView = UITableView(frame: .zero, style: .plain).then { + $0.separatorColor = .lightGray + $0.separatorStyle = .none + $0.showsVerticalScrollIndicator = false + $0.isScrollEnabled = false + } + +} +// 수평스크롤 컬렉션뷰 +extension SecondViewController: UICollectionViewDelegate {} +extension SecondViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return imageCollectionList.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let item = collectionView.dequeueReusableCell(withReuseIdentifier: ImageCollectionViewCell.identifier, + for: indexPath) as? ImageCollectionViewCell else {return UICollectionViewCell()} + item.bindData(data: imageCollectionList[indexPath.row]) + return item + } +} +extension SecondViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 3 + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 3 + } + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: (self.box.bounds.width)/5, height: self.contentview.bounds.height) + } +} +// 일주일 날씨보여주는 테이블뷰 +extension SecondViewController: UITableViewDelegate { +} +extension SecondViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return weatherListData.count + } + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: WeatherTableViewCell.identifier, + for: indexPath) as? WeatherTableViewCell else {return UITableViewCell()} + cell.bindData(data: weatherListData[indexPath.row]) + return cell + } +} + diff --git a/sopt_fourth_seminar0/Client/ViewController.swift b/sopt_fourth_seminar0/Client/ViewController.swift new file mode 100644 index 0000000..31ac276 --- /dev/null +++ b/sopt_fourth_seminar0/Client/ViewController.swift @@ -0,0 +1,154 @@ +import UIKit +import SnapKit +import Then + +class ViewController: UIViewController, WeatherInfoViewDelegate { + + var searchWeatherInfoListData = itemListData + + override func viewDidLoad() { + super.viewDidLoad() + self.navigationItem.title = "ViewController" + self.setLayout() + self.setTableViewConfig() + Task { + await fetchWeatherInfo() + } + } + + private func setTableViewConfig() { + self.tableView.register(ItemListTableViewCell.self, + forCellReuseIdentifier: ItemListTableViewCell.identifier) + self.tableView.delegate = self + self.tableView.dataSource = self + self.tableView.rowHeight = 135 // 세로길이 늘리니까 셀 간격 생김.. 이게 왜? + self.tableView.separatorStyle = .none + } + + private func fetchWeatherInfo() async { + let cities = ["seoul", "daegu", "mokpo", "chuncheon", "jeju"] + + for city in cities { + do { + guard let currentWeather = try await GetInfoService.shared.PostRegisterData(cityname: city) else {return} + let weatherInfo = ItemListData(cityName: city, location: currentWeather.name, time: currentWeather.timezone, weather: currentWeather.weather[0].main, temperature: currentWeather.main.temp, maxtemp: currentWeather.main.tempMax, mintemp: currentWeather.main.tempMin) + + itemListData.append(weatherInfo) + searchWeatherInfoListData = itemListData + } catch { + print(error) + } + } + tableView.reloadData() + } + + private func setLayout() { + [topview, tableView].forEach() { + self.view.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + NSLayoutConstraint.activate([topview.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), + topview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor), + topview.trailingAnchor.constraint(equalTo: self.view.trailingAnchor), + topview.heightAnchor.constraint(equalToConstant: 190) + ]) + NSLayoutConstraint.activate([tableView.topAnchor.constraint(equalTo: topview.bottomAnchor), + tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10), + tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10), + tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor) + ]) + } + + // tableView 이니셜라이저 인자에 값이 있어도 setLayout()에서 다시 설정해주니 괜찮은건가?..** + private let tableView = UITableView(frame: .init(), style: .grouped).then { + $0.backgroundColor = .black + $0.separatorColor = .black + $0.separatorStyle = .none + } + + lazy var topview: UIView = { + let view = UIView() + view.backgroundColor = .black + let label = UILabel() + label.text = "날씨" + label.textColor = .white + label.font = UIFont(name: "SFProDisplay-Bold", size: 36) + let button = UIButton() + button.setImage(UIImage(named: "dots"), for: .normal) + + // 검색창 + let view1 = UISearchBar() + view1.barTintColor = UIColor.black + + view1.placeholder = "도시 또는 공항 검색" + view1.layer.cornerRadius = 3 + view1.setImage(UIImage(named: "icSearchNonW"), for: UISearchBar.Icon.search, state: .normal) + view1.setImage(UIImage(named: "icCancel"), for: .clear, state: .normal) + + if let textfield = view1.value(forKey: "searchField") as? UITextField { + textfield.backgroundColor = UIColor.gray + textfield.attributedPlaceholder = NSAttributedString(string: textfield.placeholder ?? "", attributes: [NSAttributedString.Key.foregroundColor : UIColor.lightGray]) + textfield.textColor = UIColor.white + + if let leftView = textfield.leftView as? UIImageView { + leftView.image = leftView.image?.withRenderingMode(.alwaysTemplate) + leftView.tintColor = UIColor.white + } + if let rightView = textfield.rightView as? UIImageView { + rightView.image = rightView.image?.withRenderingMode(.alwaysTemplate) + rightView.tintColor = UIColor.white + } + } + + [label, button, view1].forEach() { + view.addSubview($0) + $0.translatesAutoresizingMaskIntoConstraints = false + } + NSLayoutConstraint.activate([view1.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10), + view1.trailingAnchor.constraint(equalTo: view.trailingAnchor), + view1.leadingAnchor.constraint(equalTo: view.leadingAnchor), + view1.heightAnchor.constraint(equalToConstant: 40) + ]) + NSLayoutConstraint.activate([label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), + label.bottomAnchor.constraint(equalTo: view1.topAnchor, constant: -10) + ]) + NSLayoutConstraint.activate([button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10), + button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10) + ]) + + return view + }() + + func weatherInfoViewTapped(_ weatherinfo: ItemListTableViewCell) { + guard let location = weatherinfo.locationLabel.text, + let weather = weatherinfo.weatherLabel.text, + let temperature = weatherinfo.tempLabel.text, + let maxtemp = weatherinfo.maxtemp.text, + let mintemp = weatherinfo.mintemp.text else { + return + } + + let weatherDetailedInfoVC = SecondViewController(location: location, temperature: temperature, weather: weather, maxtemp: maxtemp, mintemp: mintemp) + + self.navigationController?.pushViewController(weatherDetailedInfoVC, animated: true) + self.navigationController?.isNavigationBarHidden = true + } + +} + +// 첫 함수가 cell의 개수를 반환하고 그 cell 개수를 바탕으로 두 번째 함수를 반복..? +extension ViewController: UITableViewDelegate {} +extension ViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return itemListData.count + } + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ItemListTableViewCell.identifier, + for: indexPath) as? ItemListTableViewCell else {return UITableViewCell()} + cell.delegate = self + cell.bindData(data: itemListData[indexPath.row]) + + return cell + } +} + diff --git a/sopt_fourth_seminar0/Client/WeatherTableViewCell.swift b/sopt_fourth_seminar0/Client/WeatherTableViewCell.swift new file mode 100644 index 0000000..803d636 --- /dev/null +++ b/sopt_fourth_seminar0/Client/WeatherTableViewCell.swift @@ -0,0 +1,82 @@ +import UIKit +import SnapKit +import Then + +class WeatherTableViewCell: UITableViewCell { + + static let identifier: String = "WeatherTableViewCell" + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + self.setLayout() + self.backgroundColor = .clear + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func bindData(data: WeatherListData) { + self.dayLabel.text = data.dayLabel + self.max.text = "\(data.maxtemp)°C" + self.min.text = "\(data.mintemp)°C" + self.barImage.image = UIImage(named: data.barLabel) + self.image.image = UIImage(named: data.weatherLabel) + } + + private func setLayout() { + // cell높이 정해줌..** +// NSLayoutConstraint.activate([self.heightAnchor.constraint(equalToConstant: 60) +// ]) + + [dayLabel, image, min, max, barImage].forEach() { + $0.translatesAutoresizingMaskIntoConstraints = false + self.addSubview($0) + } + NSLayoutConstraint.activate([max.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10), + max.centerYAnchor.constraint(equalTo: self.centerYAnchor) + ]) + NSLayoutConstraint.activate([barImage.trailingAnchor.constraint(equalTo: max.leadingAnchor, constant: -10), + barImage.centerYAnchor.constraint(equalTo: self.centerYAnchor) + ]) + NSLayoutConstraint.activate([min.trailingAnchor.constraint(equalTo: barImage.leadingAnchor, constant: -10), + min.centerYAnchor.constraint(equalTo: self.centerYAnchor) + ]) + NSLayoutConstraint.activate([dayLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10), + dayLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor) + ]) + NSLayoutConstraint.activate([image.trailingAnchor.constraint(equalTo: min.leadingAnchor, constant: -30), + image.centerYAnchor.constraint(equalTo: self.centerYAnchor) + ]) + } + + let dayLabel: UILabel = { + let label = UILabel() + label.textColor = .white + label.font = UIFont(name: "SFProDisplay-Medium", size: 22) + return label + }() + let image: UIImageView = { + let image = UIImageView() + image.contentMode = .scaleAspectFit + return image + }() + let min: UILabel = { + let label = UILabel() + label.textColor = .lightGray + label.font = UIFont(name: "SFProDisplay-Medium", size: 19) + return label + }() + let max: UILabel = { + let label = UILabel() + label.textColor = .white + label.font = UIFont(name: "SFProDisplay-Medium", size: 19) + return label + }() + let barImage: UIImageView = { + let image = UIImageView() + image.contentMode = .scaleAspectFit + return image + }() + +} diff --git a/sopt_fourth_seminar0/Client/protocol.swift b/sopt_fourth_seminar0/Client/protocol.swift new file mode 100644 index 0000000..2058228 --- /dev/null +++ b/sopt_fourth_seminar0/Client/protocol.swift @@ -0,0 +1,12 @@ +// +// protocol.swift +// sopt_fourth_seminar0 +// +// Created by Woo Jye Lee on 11/15/23. +// + +import Foundation + +protocol WeatherInfoViewDelegate: AnyObject { + func weatherInfoViewTapped(_ weatherinfo: ItemListTableViewCell) +} diff --git a/sopt_fourth_seminar0/Data/HourlyInfoDataModel.swift b/sopt_fourth_seminar0/Data/HourlyInfoDataModel.swift new file mode 100644 index 0000000..bd7d76d --- /dev/null +++ b/sopt_fourth_seminar0/Data/HourlyInfoDataModel.swift @@ -0,0 +1,97 @@ +// +// HourlyInfoDataModel.swift +// sopt_fourth_seminar0 +// +// Created by Woo Jye Lee on 11/16/23. +// + +import Foundation + +struct HourlyInfoDataModel: Codable { + let cod: String + let message, cnt: Int + let list: [List] + let city: City +} + +// MARK: - City +struct City: Codable { + let id: Int + let name: String + let coord: Coord + let country: String + let population, timezone, sunrise, sunset: Int +} + +// MARK: - List +struct List: Codable { + let dt: Int + let main: MainClass + let weather: [DetailWeather] + let clouds: Clouds + let wind: Wind + let visibility: Int + let pop: Double + let sys: DetailSys + let dtTxt: String + let rain, snow: Rain? + + enum CodingKeys: String, CodingKey { + case dt, main, weather, clouds, wind, visibility, pop, sys + case dtTxt = "dt_txt" + case rain, snow + } +} + + +// MARK: - MainClass +struct MainClass: Codable { + let temp, feelsLike, tempMin, tempMax: Double + let pressure, seaLevel, grndLevel, humidity: Int + let tempKf: Double + + enum CodingKeys: String, CodingKey { + case temp + case feelsLike = "feels_like" + case tempMin = "temp_min" + case tempMax = "temp_max" + case pressure + case seaLevel = "sea_level" + case grndLevel = "grnd_level" + case humidity + case tempKf = "temp_kf" + } +} + +// MARK: - Rain +struct Rain: Codable { + let the3h: Double + + enum CodingKeys: String, CodingKey { + case the3h = "3h" + } +} + +// MARK: - Sys +struct DetailSys: Codable { + let pod: Pod +} + +enum Pod: String, Codable { + case d = "d" + case n = "n" +} + +// MARK: - Weather +struct DetailWeather: Codable { + let id: Int + let main: MainEnum + let description, icon: String +} + +enum MainEnum: String, Codable { + case clear = "Clear" + case clouds = "Clouds" + case rain = "Rain" + case snow = "Snow" +} diff --git a/sopt_fourth_seminar0/Data/ImageCollectionData.swift b/sopt_fourth_seminar0/Data/ImageCollectionData.swift new file mode 100644 index 0000000..dbbbe67 --- /dev/null +++ b/sopt_fourth_seminar0/Data/ImageCollectionData.swift @@ -0,0 +1,34 @@ + +// 시간대별 날씨 정보 데이터모델 +import Foundation + +struct ImageCollectionData { + let time: String + let weather: String // weather icon + let temperature: Int + + init(time: String, weather: String, temperature: Double) { + self.time = extractHour(from: time)! + self.weather = weather + self.temperature = convertTemperature2(temperature: temperature) + } +} +// invalid redeclarion of function error +func convertTemperature2(temperature: Double) -> Int { + return Int(round((temperature - 273.15))) +} + +func extractHour(from dateString: String) -> String? { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + + if let date = dateFormatter.date(from: dateString) { + let calendar = Calendar.current + let hour = calendar.component(.hour, from: date) + return String(format: "%02d시", hour) + } + + return nil +} + +var imageCollectionList: [ImageCollectionData] = [] diff --git a/sopt_fourth_seminar0/Data/ItemListData.swift b/sopt_fourth_seminar0/Data/ItemListData.swift new file mode 100644 index 0000000..0de3d4f --- /dev/null +++ b/sopt_fourth_seminar0/Data/ItemListData.swift @@ -0,0 +1,36 @@ +import Foundation + +struct ItemListData: Equatable { + // 이 구조체에서 time이 필요한 이유..? + let cityName: String + let location: String + let time: String + let weather: String + let temperature: Int + let maxtemp: Int + let mintemp: Int + + init(cityName: String, location: String, time: Int, weather: String, temperature: Double, maxtemp: Double, mintemp: Double) { + self.cityName = cityName + self.time = convertTime(timezone: time) + self.location = location + self.maxtemp = convertTemperature(temperature: maxtemp) + self.mintemp = convertTemperature(temperature: mintemp) + self.weather = weather + self.temperature = convertTemperature(temperature: temperature) + } +} +func convertTime(timezone: Int) -> String { + let timeZone = TimeZone(secondsFromGMT: timezone) + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "HH:mm" + dateFormatter.timeZone = timeZone + let currentDate = Date() + let formattedTime = dateFormatter.string(from: currentDate) + return formattedTime +} +func convertTemperature(temperature: Double) -> Int { + return Int(round((temperature - 273.15))) +} + +var itemListData: [ItemListData] = [] diff --git a/sopt_fourth_seminar0/Data/UserInfoDataModel.swift b/sopt_fourth_seminar0/Data/UserInfoDataModel.swift new file mode 100644 index 0000000..8520c8d --- /dev/null +++ b/sopt_fourth_seminar0/Data/UserInfoDataModel.swift @@ -0,0 +1,62 @@ + +import Foundation + +// 서버에서 받은 JSON 객체를 어떤 형태로 변형할 것인지? 에 대한 구조체 (홈화면 날씨 정보 구조체) +// openweather에서 완전체 response 형태를 받아오는 방법은? +struct UserInfoDataModel: Codable { + let coord: Coord + let weather: [Weather] + let base: String + let main: Main + let visibility: Int + let wind: Wind + let clouds: Clouds + let dt: Int + let sys: Sys + let timezone, id: Int + let name: String + let cod: Int +} + +// MARK: - Clouds +struct Clouds: Codable { + let all: Int +} + +// MARK: - Coord +struct Coord: Codable { + let lon, lat: Double +} + +// MARK: - Main +struct Main: Codable { + let temp, feelsLike, tempMin, tempMax: Double + let pressure, humidity: Int + + enum CodingKeys: String, CodingKey { + case temp + case feelsLike = "feels_like" + case tempMin = "temp_min" + case tempMax = "temp_max" + case pressure, humidity + } +} + +// MARK: - Sys +struct Sys: Codable { + let type, id: Int + let country: String + let sunrise, sunset: Int +} + +// MARK: - Weather +struct Weather: Codable { + let id: Int + let main, description, icon: String +} + +// MARK: - Wind +struct Wind: Codable { + let speed: Double + let deg: Int +} diff --git a/sopt_fourth_seminar0/Data/WeatherListData.swift b/sopt_fourth_seminar0/Data/WeatherListData.swift new file mode 100644 index 0000000..6985b2e --- /dev/null +++ b/sopt_fourth_seminar0/Data/WeatherListData.swift @@ -0,0 +1,69 @@ +import Foundation + +struct WeatherListData { + let dayLabel: String + let maxtemp: String + let mintemp: String + let weatherLabel: String + let barLabel: String + + init(dayLabel: String, maxtemp: String, mintemp: String, weatherLabel: String, barLabel: String) { + self.dayLabel = dayLabel + self.maxtemp = maxtemp + self.mintemp = mintemp + self.weatherLabel = weatherLabel + self.barLabel = barLabel + } +} + +var weatherListData: [WeatherListData] = [.init(dayLabel: "오늘", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar1"), + .init(dayLabel: "월", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar2"), + .init(dayLabel: "화", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar3"), + .init(dayLabel: "수", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar4"), + .init(dayLabel: "목", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar5"), + .init(dayLabel: "금", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar6"), + .init(dayLabel: "토", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar7"), + .init(dayLabel: "일", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar8"), + .init(dayLabel: "월", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar9"), + .init(dayLabel: "화", + maxtemp: "20", + mintemp: "10", + weatherLabel: "rain", + barLabel: "bar10") +] diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Black.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Black.otf new file mode 100644 index 0000000..6466400 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Black.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-BlackItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-BlackItalic.otf new file mode 100644 index 0000000..b0718f3 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-BlackItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Bold.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Bold.otf new file mode 100644 index 0000000..21aacc1 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Bold.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-BoldItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-BoldItalic.otf new file mode 100644 index 0000000..85a137e Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-BoldItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Heavy.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Heavy.otf new file mode 100644 index 0000000..6a780c0 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Heavy.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-HeavyItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-HeavyItalic.otf new file mode 100644 index 0000000..cabe71a Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-HeavyItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Light.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Light.otf new file mode 100644 index 0000000..1a3f4d4 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Light.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-LightItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-LightItalic.otf new file mode 100644 index 0000000..da2fe27 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-LightItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Medium.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Medium.otf new file mode 100644 index 0000000..cf88e12 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Medium.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-MediumItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-MediumItalic.otf new file mode 100644 index 0000000..6f67f25 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-MediumItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Regular.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Regular.otf new file mode 100644 index 0000000..ef46a6a Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Regular.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-RegularItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-RegularItalic.otf new file mode 100644 index 0000000..858963b Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-RegularItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Semibold.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Semibold.otf new file mode 100644 index 0000000..6a957ab Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Semibold.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-SemiboldItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-SemiboldItalic.otf new file mode 100644 index 0000000..02a3b86 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-SemiboldItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Thin.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Thin.otf new file mode 100644 index 0000000..57dd502 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Thin.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-ThinItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-ThinItalic.otf new file mode 100644 index 0000000..e5b41cc Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-ThinItalic.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Ultralight.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Ultralight.otf new file mode 100644 index 0000000..3ecdfc7 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-Ultralight.otf differ diff --git a/sopt_fourth_seminar0/Fonts/SF-Pro-Display-UltralightItalic.otf b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-UltralightItalic.otf new file mode 100644 index 0000000..98f2941 Binary files /dev/null and b/sopt_fourth_seminar0/Fonts/SF-Pro-Display-UltralightItalic.otf differ diff --git a/sopt_fourth_seminar0/Info.plist b/sopt_fourth_seminar0/Info.plist new file mode 100644 index 0000000..0e547da --- /dev/null +++ b/sopt_fourth_seminar0/Info.plist @@ -0,0 +1,36 @@ + + + + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIAppFonts + + SF-Pro-Display-Bold.otf + SF-Pro-Display-Thin.otf + SF-Pro-Display-Light.otf + SF-Pro-Display-Medium.otf + SF-Pro-Display-Regular.otf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + diff --git a/sopt_fourth_seminar0/SceneDelegate.swift b/sopt_fourth_seminar0/SceneDelegate.swift new file mode 100644 index 0000000..de803fd --- /dev/null +++ b/sopt_fourth_seminar0/SceneDelegate.swift @@ -0,0 +1,62 @@ +// +// SceneDelegate.swift +// sopt_fourth_seminar0 +// +// Created by Woo Jye Lee on 11/15/23. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let scene = (scene as? UIWindowScene) else { return } + window = UIWindow(frame: scene.coordinateSpace.bounds) + window?.windowScene = scene + + // root view controller 지정 + let rootViewController = ViewController() + + // 네비게이션 컨트롤러 생성 + let navigationController = UINavigationController(rootViewController: rootViewController) + window?.rootViewController = navigationController + window?.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/sopt_fourth_seminar0/Server/GetInfoService.swift b/sopt_fourth_seminar0/Server/GetInfoService.swift new file mode 100644 index 0000000..5e90e81 --- /dev/null +++ b/sopt_fourth_seminar0/Server/GetInfoService.swift @@ -0,0 +1,65 @@ +// +// GetInfoService.swift +// sopt_fourth_seminar0 +// +// Created by Woo Jye Lee on 11/15/23. +// + +// 홈화면 tableviewcell의 날씨정보를 가져옵니다. +import Foundation + +class GetInfoService { + static let shared = GetInfoService() + private init() {} + let APIkey: String = "c2cad16c620c4af5508892bc7357e0fc" + + func makeRequest(cityname: String) -> URLRequest { + let url = URL(string: "https://api.openweathermap.org/data/2.5/weather?q=\(cityname)&appid=\(APIkey)")! + var request = URLRequest(url: url) + request.httpMethod = "GET" + let header = ["Content-Type": "application/json"] + header.forEach { + request.addValue($0.value, forHTTPHeaderField: $0.key) + } + return request + } + + // 조회만 하면 되서 POST는 사용하지 않아도됨(?) -> 아래 코드까지 해줘야 서버의 데이터를 얻어올 수 있는듯. + func PostRegisterData(cityname: String) async throws -> UserInfoDataModel? { + do { + let request = self.makeRequest(cityname: cityname) + let (data, response) = try await URLSession.shared.data(for: request) + dump(request) + guard let httpResponse = response as? HTTPURLResponse else { + throw NetworkError.responseError + } + dump(response) + guard let parseData = parseUserInfoData(data: data) + else { + throw NetworkError.responseDecodingError + } + return parseData + } catch { + throw error + } + + } + + // server로부터 가져윤 JSON을 구조체로 바꾸는 과정 + private func parseUserInfoData(data: Data) -> UserInfoDataModel? { + do { + let jsonDecoder = JSONDecoder() + let result = try jsonDecoder.decode(UserInfoDataModel.self, from: data) + return result + } catch { + print(error) + return nil + } + } + + private func configureHTTPError(errorCode: Int) -> Error { + return NetworkError(rawValue: errorCode) + ?? NetworkError.unknownError + } + +} diff --git a/sopt_fourth_seminar0/Server/GetTimeline.swift b/sopt_fourth_seminar0/Server/GetTimeline.swift new file mode 100644 index 0000000..dfd900e --- /dev/null +++ b/sopt_fourth_seminar0/Server/GetTimeline.swift @@ -0,0 +1,62 @@ +// +// GetTimeline.swift +// sopt_fourth_seminar0 +// +// Created by Woo Jye Lee on 11/16/23. +// + +import Foundation + +class GetTimeline { + static let shared = GetTimeline() + private init() {} + let APIkey: String = "c2cad16c620c4af5508892bc7357e0fc" + + func makeRequest(cityname: String) -> URLRequest { + let url = URL(string: "https://api.openweathermap.org/data/2.5/forecast?q=\(cityname)&appid=\(APIkey)")! + var request = URLRequest(url: url) + request.httpMethod = "GET" + let header = ["Content-Type": "application/json"] + header.forEach { + request.addValue($0.value, forHTTPHeaderField: $0.key) + } + return request + } + + func GetTimelineData(cityname: String) async throws -> HourlyInfoDataModel? { + do { + let request = self.makeRequest(cityname: cityname) + let (data, response) = try await URLSession.shared.data(for: request) + dump(request) + guard let httpResponse = response as? HTTPURLResponse else { + throw NetworkError.responseError + } + dump(response) + guard let parseData = parseUserInfoData(data: data) + else { + throw NetworkError.responseDecodingError + } + return parseData + } catch { + throw error + } + + } + + private func parseUserInfoData(data: Data) -> HourlyInfoDataModel? { + do { + let jsonDecoder = JSONDecoder() + let result = try jsonDecoder.decode(HourlyInfoDataModel.self, from: data) + return result + } catch { + print(error) + return nil + } + } + + private func configureHTTPError(errorCode: Int) -> Error { + return NetworkError(rawValue: errorCode) + ?? NetworkError.unknownError + } + +} diff --git a/sopt_fourth_seminar0/Server/NetworkError.swift b/sopt_fourth_seminar0/Server/NetworkError.swift new file mode 100644 index 0000000..cfd3a10 --- /dev/null +++ b/sopt_fourth_seminar0/Server/NetworkError.swift @@ -0,0 +1,22 @@ +enum NetworkError: Int, Error, CustomStringConvertible { + var description: String { self.errorDescription } + case requstEncodingError + case responseDecodingError + case responseError + case unknownError + case loginFailed = 400 + case internalServerError = 500 + case notFoundError = 404 + + var errorDescription: String { + switch self { + case .loginFailed: return "로그인에 실패하였습니다." + case .requstEncodingError: return "REQUEST_ENCODING_ERROR" + case .responseError: return "RESPONSE_ERROR" + case .responseDecodingError: return "RESPONSE_DECODING_ERROR" + case .unknownError: return "UNKNOWN_ERROR" + case .internalServerError: return "500:INTERNAL_SERVER_ERROR" + case .notFoundError: return "404:NOT_FOUND_ERROR" + } + } +}