diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..36bbda7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.pbxproj binary merge=union + diff --git a/DiaryApp.xcodeproj/project.pbxproj b/DiaryApp.xcodeproj/project.pbxproj index 1631a8b..1087db6 100644 --- a/DiaryApp.xcodeproj/project.pbxproj +++ b/DiaryApp.xcodeproj/project.pbxproj @@ -7,11 +7,23 @@ objects = { /* Begin PBXBuildFile section */ + 416BCE5008D8A336C839F81F /* Pods_DiaryApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6ECBBCC35FC9003BDB3F2719 /* Pods_DiaryApp.framework */; }; + 4E1112492AA45E7A00D4E550 /* weatherAPI.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4E1112482AA45E7A00D4E550 /* weatherAPI.plist */; }; + 4E1550842A9E049100C8A500 /* DiaryListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1550832A9E049100C8A500 /* DiaryListCell.swift */; }; + 4E1550892A9E0C6500C8A500 /* CustomStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E1550882A9E0C6500C8A500 /* CustomStackView.swift */; }; + 4E15508B2A9E0DF500C8A500 /* CustomLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E15508A2A9E0DF500C8A500 /* CustomLabel.swift */; }; + 4E6138B02A9EEF50003A2B37 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4E6138AF2A9EEF50003A2B37 /* SnapKit */; }; + 4E6138C02A9F9E8B003A2B37 /* DiaryListThumbnailCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6138BD2A9F9E8B003A2B37 /* DiaryListThumbnailCell.swift */; }; + 4E6138C12A9F9E8B003A2B37 /* DiaryProgressCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6138BE2A9F9E8B003A2B37 /* DiaryProgressCell.swift */; }; + 4E6138C42A9F9FBA003A2B37 /* CircleProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6138C32A9F9FBA003A2B37 /* CircleProgressBar.swift */; }; + 4E6138C92A9FA164003A2B37 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4E6138C82A9FA164003A2B37 /* LaunchScreen.storyboard */; }; + 4E7A15042AA0A9C8005FCD9C /* ProgressListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7A15032AA0A9C8005FCD9C /* ProgressListCell.swift */; }; + 4E7A15062AA0BFA1005FCD9C /* ProgressTagCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7A15052AA0BFA1005FCD9C /* ProgressTagCell.swift */; }; + 4E7A150A2AA0C11E005FCD9C /* HomeFeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E7A15092AA0C11E005FCD9C /* HomeFeedCell.swift */; }; 4E98A38F2A9CA57B003052D3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A38E2A9CA57B003052D3 /* AppDelegate.swift */; }; 4E98A3912A9CA57B003052D3 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3902A9CA57B003052D3 /* SceneDelegate.swift */; }; 4E98A3932A9CA57B003052D3 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3922A9CA57B003052D3 /* TabBarController.swift */; }; 4E98A3982A9CA57E003052D3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4E98A3972A9CA57E003052D3 /* Assets.xcassets */; }; - 4E98A39B2A9CA57E003052D3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4E98A3992A9CA57E003052D3 /* LaunchScreen.storyboard */; }; 4E98A3A32A9CA634003052D3 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3A22A9CA634003052D3 /* HomeViewController.swift */; }; 4E98A3A62A9CA6A6003052D3 /* NewDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3A52A9CA6A6003052D3 /* NewDiaryViewController.swift */; }; 4E98A3A82A9CA6B6003052D3 /* EditDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3A72A9CA6B6003052D3 /* EditDiaryViewController.swift */; }; @@ -19,19 +31,39 @@ 4E98A3AC2A9CA70B003052D3 /* DiaryListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3AB2A9CA70B003052D3 /* DiaryListViewController.swift */; }; 4E98A3AE2A9CA748003052D3 /* DetailDiaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3AD2A9CA748003052D3 /* DetailDiaryViewController.swift */; }; 4E98A3B02A9CA762003052D3 /* DiaryProgressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3AF2A9CA762003052D3 /* DiaryProgressViewController.swift */; }; - 4E98A3B22A9CA781003052D3 /* DiaryCalenderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3B12A9CA781003052D3 /* DiaryCalenderViewController.swift */; }; 4E98A3B62A9CAA16003052D3 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3B52A9CAA16003052D3 /* ProfileViewController.swift */; }; 4E98A3B82A9CAD27003052D3 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3B72A9CAD27003052D3 /* User.swift */; }; 4E98A3BA2A9CAD60003052D3 /* Weather.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3B92A9CAD60003052D3 /* Weather.swift */; }; + 4E98A3BD2A9CB918003052D3 /* CustomCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3BC2A9CB918003052D3 /* CustomCollectionView.swift */; }; + 4E98A3C12A9CCFB8003052D3 /* CustomImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E98A3C02A9CCFB8003052D3 /* CustomImageView.swift */; }; + 4EA8081E2AA1D41100A4D465 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA8081D2AA1D41100A4D465 /* FirebaseAuth */; }; + 4EA808202AA1D41100A4D465 /* FirebaseDatabase in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA8081F2AA1D41100A4D465 /* FirebaseDatabase */; }; + 4EA808222AA1D41100A4D465 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA808212AA1D41100A4D465 /* FirebaseFirestore */; }; + 4EA808242AA1D41100A4D465 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA808232AA1D41100A4D465 /* FirebaseStorage */; }; + 4EA808262AA1D46800A4D465 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4EA808252AA1D46800A4D465 /* GoogleService-Info.plist */; }; + 4ED3122E2AA46BA700677EE0 /* WeatherManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED3122D2AA46BA700677EE0 /* WeatherManager.swift */; }; + 4ED312302AA48BFB00677EE0 /* WeatherResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED3122F2AA48BFB00677EE0 /* WeatherResponse.swift */; }; + FF0F824A2AA0D01C005CFB57 /* DiaryCalendarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0F82492AA0D01C005CFB57 /* DiaryCalendarViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 29E4E142F65D379C5B088A3D /* Pods-DiaryApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DiaryApp.release.xcconfig"; path = "Target Support Files/Pods-DiaryApp/Pods-DiaryApp.release.xcconfig"; sourceTree = ""; }; + 4E1112482AA45E7A00D4E550 /* weatherAPI.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = weatherAPI.plist; sourceTree = ""; }; + 4E1550832A9E049100C8A500 /* DiaryListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryListCell.swift; sourceTree = ""; }; + 4E1550882A9E0C6500C8A500 /* CustomStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomStackView.swift; sourceTree = ""; }; + 4E15508A2A9E0DF500C8A500 /* CustomLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomLabel.swift; sourceTree = ""; }; + 4E6138BD2A9F9E8B003A2B37 /* DiaryListThumbnailCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiaryListThumbnailCell.swift; sourceTree = ""; }; + 4E6138BE2A9F9E8B003A2B37 /* DiaryProgressCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiaryProgressCell.swift; sourceTree = ""; }; + 4E6138C32A9F9FBA003A2B37 /* CircleProgressBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleProgressBar.swift; sourceTree = ""; }; + 4E6138C82A9FA164003A2B37 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 4E7A15032AA0A9C8005FCD9C /* ProgressListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressListCell.swift; sourceTree = ""; }; + 4E7A15052AA0BFA1005FCD9C /* ProgressTagCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressTagCell.swift; sourceTree = ""; }; + 4E7A15092AA0C11E005FCD9C /* HomeFeedCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeFeedCell.swift; sourceTree = ""; }; 4E98A38B2A9CA57B003052D3 /* DiaryApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DiaryApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4E98A38E2A9CA57B003052D3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 4E98A3902A9CA57B003052D3 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 4E98A3922A9CA57B003052D3 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 4E98A3972A9CA57E003052D3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 4E98A39A2A9CA57E003052D3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 4E98A39C2A9CA57E003052D3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 4E98A3A22A9CA634003052D3 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; 4E98A3A52A9CA6A6003052D3 /* NewDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewDiaryViewController.swift; sourceTree = ""; }; @@ -40,10 +72,17 @@ 4E98A3AB2A9CA70B003052D3 /* DiaryListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryListViewController.swift; sourceTree = ""; }; 4E98A3AD2A9CA748003052D3 /* DetailDiaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailDiaryViewController.swift; sourceTree = ""; }; 4E98A3AF2A9CA762003052D3 /* DiaryProgressViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryProgressViewController.swift; sourceTree = ""; }; - 4E98A3B12A9CA781003052D3 /* DiaryCalenderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiaryCalenderViewController.swift; sourceTree = ""; }; 4E98A3B52A9CAA16003052D3 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 4E98A3B72A9CAD27003052D3 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 4E98A3B92A9CAD60003052D3 /* Weather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weather.swift; sourceTree = ""; }; + 4E98A3BC2A9CB918003052D3 /* CustomCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomCollectionView.swift; sourceTree = ""; }; + 4E98A3C02A9CCFB8003052D3 /* CustomImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomImageView.swift; sourceTree = ""; }; + 4EA808252AA1D46800A4D465 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 4ED3122D2AA46BA700677EE0 /* WeatherManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherManager.swift; sourceTree = ""; }; + 4ED3122F2AA48BFB00677EE0 /* WeatherResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherResponse.swift; sourceTree = ""; }; + 6ECBBCC35FC9003BDB3F2719 /* Pods_DiaryApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DiaryApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F45C74DA7EACBE351DEA5098 /* Pods-DiaryApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DiaryApp.debug.xcconfig"; path = "Target Support Files/Pods-DiaryApp/Pods-DiaryApp.debug.xcconfig"; sourceTree = ""; }; + FF0F82492AA0D01C005CFB57 /* DiaryCalendarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiaryCalendarViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,17 +90,38 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4EA808202AA1D41100A4D465 /* FirebaseDatabase in Frameworks */, + 4EA808222AA1D41100A4D465 /* FirebaseFirestore in Frameworks */, + 4E6138B02A9EEF50003A2B37 /* SnapKit in Frameworks */, + 4EA808242AA1D41100A4D465 /* FirebaseStorage in Frameworks */, + 4EA8081E2AA1D41100A4D465 /* FirebaseAuth in Frameworks */, + 416BCE5008D8A336C839F81F /* Pods_DiaryApp.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4E1550852A9E0BBB00C8A500 /* Cells */ = { + isa = PBXGroup; + children = ( + 4E1550832A9E049100C8A500 /* DiaryListCell.swift */, + 4E6138BD2A9F9E8B003A2B37 /* DiaryListThumbnailCell.swift */, + 4E7A15052AA0BFA1005FCD9C /* ProgressTagCell.swift */, + 4E6138BE2A9F9E8B003A2B37 /* DiaryProgressCell.swift */, + 4E7A15032AA0A9C8005FCD9C /* ProgressListCell.swift */, + 4E7A15092AA0C11E005FCD9C /* HomeFeedCell.swift */, + ); + path = Cells; + sourceTree = ""; + }; 4E98A3822A9CA57B003052D3 = { isa = PBXGroup; children = ( 4E98A38D2A9CA57B003052D3 /* DiaryApp */, 4E98A38C2A9CA57B003052D3 /* Products */, + 645148A477D85E4A768623AC /* Pods */, + C495D0DD606D22FB491EDCDE /* Frameworks */, ); sourceTree = ""; }; @@ -81,6 +141,7 @@ 4E98A3B42A9CA7AD003052D3 /* Views */, 4E98A3A42A9CA638003052D3 /* Controllers */, 4E98A39C2A9CA57E003052D3 /* Info.plist */, + 4EA808252AA1D46800A4D465 /* GoogleService-Info.plist */, ); path = DiaryApp; sourceTree = ""; @@ -92,12 +153,14 @@ 4E98A3A22A9CA634003052D3 /* HomeViewController.swift */, 4E98A3A52A9CA6A6003052D3 /* NewDiaryViewController.swift */, 4E98A3A72A9CA6B6003052D3 /* EditDiaryViewController.swift */, + FF0F82492AA0D01C005CFB57 /* DiaryCalendarViewController.swift */, 4E98A3A92A9CA6F6003052D3 /* ProgressListViewController.swift */, 4E98A3AB2A9CA70B003052D3 /* DiaryListViewController.swift */, 4E98A3AD2A9CA748003052D3 /* DetailDiaryViewController.swift */, + 4ED312312AA48CFA00677EE0 /* Weather */, 4E98A3AF2A9CA762003052D3 /* DiaryProgressViewController.swift */, - 4E98A3B12A9CA781003052D3 /* DiaryCalenderViewController.swift */, 4E98A3B52A9CAA16003052D3 /* ProfileViewController.swift */, + 4E1112482AA45E7A00D4E550 /* weatherAPI.plist */, ); path = Controllers; sourceTree = ""; @@ -114,7 +177,12 @@ 4E98A3B42A9CA7AD003052D3 /* Views */ = { isa = PBXGroup; children = ( - 4E98A3992A9CA57E003052D3 /* LaunchScreen.storyboard */, + 4E98A3BC2A9CB918003052D3 /* CustomCollectionView.swift */, + 4E1550882A9E0C6500C8A500 /* CustomStackView.swift */, + 4E15508A2A9E0DF500C8A500 /* CustomLabel.swift */, + 4E6138C32A9F9FBA003A2B37 /* CircleProgressBar.swift */, + 4E98A3C02A9CCFB8003052D3 /* CustomImageView.swift */, + 4E1550852A9E0BBB00C8A500 /* Cells */, ); path = Views; sourceTree = ""; @@ -125,10 +193,37 @@ 4E98A38E2A9CA57B003052D3 /* AppDelegate.swift */, 4E98A3902A9CA57B003052D3 /* SceneDelegate.swift */, 4E98A3972A9CA57E003052D3 /* Assets.xcassets */, + 4E6138C82A9FA164003A2B37 /* LaunchScreen.storyboard */, ); path = Resources; sourceTree = ""; }; + 4ED312312AA48CFA00677EE0 /* Weather */ = { + isa = PBXGroup; + children = ( + 4ED3122D2AA46BA700677EE0 /* WeatherManager.swift */, + 4ED3122F2AA48BFB00677EE0 /* WeatherResponse.swift */, + ); + path = Weather; + sourceTree = ""; + }; + 645148A477D85E4A768623AC /* Pods */ = { + isa = PBXGroup; + children = ( + F45C74DA7EACBE351DEA5098 /* Pods-DiaryApp.debug.xcconfig */, + 29E4E142F65D379C5B088A3D /* Pods-DiaryApp.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + C495D0DD606D22FB491EDCDE /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6ECBBCC35FC9003BDB3F2719 /* Pods_DiaryApp.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -136,15 +231,24 @@ isa = PBXNativeTarget; buildConfigurationList = 4E98A39F2A9CA57E003052D3 /* Build configuration list for PBXNativeTarget "DiaryApp" */; buildPhases = ( + 8DFF7EF8445834A3CB02DCFD /* [CP] Check Pods Manifest.lock */, 4E98A3872A9CA57B003052D3 /* Sources */, 4E98A3882A9CA57B003052D3 /* Frameworks */, 4E98A3892A9CA57B003052D3 /* Resources */, + FA407FDDD7445C4F63B1AD88 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); dependencies = ( ); name = DiaryApp; + packageProductDependencies = ( + 4E6138AF2A9EEF50003A2B37 /* SnapKit */, + 4EA8081D2AA1D41100A4D465 /* FirebaseAuth */, + 4EA8081F2AA1D41100A4D465 /* FirebaseDatabase */, + 4EA808212AA1D41100A4D465 /* FirebaseFirestore */, + 4EA808232AA1D41100A4D465 /* FirebaseStorage */, + ); productName = DiaryApp; productReference = 4E98A38B2A9CA57B003052D3 /* DiaryApp.app */; productType = "com.apple.product-type.application"; @@ -173,6 +277,10 @@ Base, ); mainGroup = 4E98A3822A9CA57B003052D3; + packageReferences = ( + 4E6138AE2A9EEF50003A2B37 /* XCRemoteSwiftPackageReference "SnapKit" */, + 4EA8081C2AA1D41100A4D465 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + ); productRefGroup = 4E98A38C2A9CA57B003052D3 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -187,48 +295,94 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4E98A39B2A9CA57E003052D3 /* LaunchScreen.storyboard in Resources */, + 4E1112492AA45E7A00D4E550 /* weatherAPI.plist in Resources */, 4E98A3982A9CA57E003052D3 /* Assets.xcassets in Resources */, + 4E6138C92A9FA164003A2B37 /* LaunchScreen.storyboard in Resources */, + 4EA808262AA1D46800A4D465 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 8DFF7EF8445834A3CB02DCFD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-DiaryApp-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + FA407FDDD7445C4F63B1AD88 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-DiaryApp/Pods-DiaryApp-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-DiaryApp/Pods-DiaryApp-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-DiaryApp/Pods-DiaryApp-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 4E98A3872A9CA57B003052D3 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4E7A150A2AA0C11E005FCD9C /* HomeFeedCell.swift in Sources */, + 4E98A3BD2A9CB918003052D3 /* CustomCollectionView.swift in Sources */, 4E98A3932A9CA57B003052D3 /* TabBarController.swift in Sources */, 4E98A38F2A9CA57B003052D3 /* AppDelegate.swift in Sources */, 4E98A3912A9CA57B003052D3 /* SceneDelegate.swift in Sources */, 4E98A3A32A9CA634003052D3 /* HomeViewController.swift in Sources */, - 4E98A3B22A9CA781003052D3 /* DiaryCalenderViewController.swift in Sources */, 4E98A3A82A9CA6B6003052D3 /* EditDiaryViewController.swift in Sources */, + 4ED312302AA48BFB00677EE0 /* WeatherResponse.swift in Sources */, + 4E1550842A9E049100C8A500 /* DiaryListCell.swift in Sources */, + 4E6138C02A9F9E8B003A2B37 /* DiaryListThumbnailCell.swift in Sources */, 4E98A3BA2A9CAD60003052D3 /* Weather.swift in Sources */, 4E98A3B82A9CAD27003052D3 /* User.swift in Sources */, + 4E7A15062AA0BFA1005FCD9C /* ProgressTagCell.swift in Sources */, + 4E7A15042AA0A9C8005FCD9C /* ProgressListCell.swift in Sources */, + 4E6138C12A9F9E8B003A2B37 /* DiaryProgressCell.swift in Sources */, + 4ED3122E2AA46BA700677EE0 /* WeatherManager.swift in Sources */, 4E98A3B62A9CAA16003052D3 /* ProfileViewController.swift in Sources */, + 4E15508B2A9E0DF500C8A500 /* CustomLabel.swift in Sources */, + 4E1550892A9E0C6500C8A500 /* CustomStackView.swift in Sources */, 4E98A3AE2A9CA748003052D3 /* DetailDiaryViewController.swift in Sources */, 4E98A3AC2A9CA70B003052D3 /* DiaryListViewController.swift in Sources */, 4E98A3B02A9CA762003052D3 /* DiaryProgressViewController.swift in Sources */, + 4E6138C42A9F9FBA003A2B37 /* CircleProgressBar.swift in Sources */, 4E98A3A62A9CA6A6003052D3 /* NewDiaryViewController.swift in Sources */, + 4E98A3C12A9CCFB8003052D3 /* CustomImageView.swift in Sources */, + FF0F824A2AA0D01C005CFB57 /* DiaryCalendarViewController.swift in Sources */, 4E98A3AA2A9CA6F6003052D3 /* ProgressListViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - 4E98A3992A9CA57E003052D3 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4E98A39A2A9CA57E003052D3 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ 4E98A39D2A9CA57E003052D3 /* Debug */ = { isa = XCBuildConfiguration; @@ -346,6 +500,7 @@ }; 4E98A3A02A9CA57E003052D3 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F45C74DA7EACBE351DEA5098 /* Pods-DiaryApp.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -374,6 +529,7 @@ }; 4E98A3A12A9CA57E003052D3 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 29E4E142F65D379C5B088A3D /* Pods-DiaryApp.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -422,6 +578,53 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 4E6138AE2A9EEF50003A2B37 /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; + 4EA8081C2AA1D41100A4D465 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 10.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4E6138AF2A9EEF50003A2B37 /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 4E6138AE2A9EEF50003A2B37 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; + 4EA8081D2AA1D41100A4D465 /* FirebaseAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 4EA8081C2AA1D41100A4D465 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 4EA8081F2AA1D41100A4D465 /* FirebaseDatabase */ = { + isa = XCSwiftPackageProductDependency; + package = 4EA8081C2AA1D41100A4D465 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseDatabase; + }; + 4EA808212AA1D41100A4D465 /* FirebaseFirestore */ = { + isa = XCSwiftPackageProductDependency; + package = 4EA8081C2AA1D41100A4D465 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseFirestore; + }; + 4EA808232AA1D41100A4D465 /* FirebaseStorage */ = { + isa = XCSwiftPackageProductDependency; + package = 4EA8081C2AA1D41100A4D465 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseStorage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 4E98A3832A9CA57B003052D3 /* Project object */; } diff --git a/DiaryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DiaryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..49740e1 --- /dev/null +++ b/DiaryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,122 @@ +{ + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c", + "version" : "1.2022062300.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk.git", + "state" : { + "revision" : "2bfe6abe1014aafe5cf28401708f7d39f9926a76", + "version" : "10.14.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "03b9beee1a61f62d32c521e172e192a1663a5e8b", + "version" : "10.13.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "aae45a320fd0d11811820335b1eabc8753902a40", + "version" : "9.2.5" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "c38ce365d77b04a9a300c31061c5227589e5597b", + "version" : "7.11.5" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "f1b366129d1125be7db83247e003fc333104b569", + "version" : "1.50.2" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "d415594121c9e8a4f9d79cecee0965cf35e74dbd", + "version" : "3.1.1" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", + "version" : "1.22.2" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", + "version" : "2.30909.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", + "version" : "2.3.1" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "f222cbdf325885926566172f6f5f06af95473158", + "version" : "5.6.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "cf62cdaea48b77f1a631e5cb3aeda6047c2cba1d", + "version" : "1.23.0" + } + } + ], + "version" : 2 +} diff --git a/DiaryApp.xcworkspace/contents.xcworkspacedata b/DiaryApp.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..fe90328 --- /dev/null +++ b/DiaryApp.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/DiaryApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/DiaryApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/DiaryApp.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/DiaryApp.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DiaryApp.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..49740e1 --- /dev/null +++ b/DiaryApp.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,122 @@ +{ + "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c", + "version" : "1.2022062300.0" + } + }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk.git", + "state" : { + "revision" : "2bfe6abe1014aafe5cf28401708f7d39f9926a76", + "version" : "10.14.0" + } + }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "03b9beee1a61f62d32c521e172e192a1663a5e8b", + "version" : "10.13.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "aae45a320fd0d11811820335b1eabc8753902a40", + "version" : "9.2.5" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "c38ce365d77b04a9a300c31061c5227589e5597b", + "version" : "7.11.5" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "f1b366129d1125be7db83247e003fc333104b569", + "version" : "1.50.2" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "d415594121c9e8a4f9d79cecee0965cf35e74dbd", + "version" : "3.1.1" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", + "version" : "1.22.2" + } + }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", + "version" : "2.30909.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", + "version" : "2.3.1" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "f222cbdf325885926566172f6f5f06af95473158", + "version" : "5.6.0" + } + }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "cf62cdaea48b77f1a631e5cb3aeda6047c2cba1d", + "version" : "1.23.0" + } + } + ], + "version" : 2 +} diff --git a/DiaryApp/Controllers/DetailDiaryViewController.swift b/DiaryApp/Controllers/DetailDiaryViewController.swift index 860de8f..ef0f52f 100644 --- a/DiaryApp/Controllers/DetailDiaryViewController.swift +++ b/DiaryApp/Controllers/DetailDiaryViewController.swift @@ -10,5 +10,6 @@ import UIKit class DetailDiaryViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground } } diff --git a/DiaryApp/Controllers/DiaryCalendarViewController.swift b/DiaryApp/Controllers/DiaryCalendarViewController.swift new file mode 100644 index 0000000..581e77c --- /dev/null +++ b/DiaryApp/Controllers/DiaryCalendarViewController.swift @@ -0,0 +1,488 @@ +// +// BottomSheetViewController.swift +// DiaryApp +// +// Created by t2023-m0056 on 2023/08/31. +// + +import UIKit +import SnapKit + +@available(iOS 16.0, *) +class DiaryCalendarViewController: UIViewController { + + // MARK: - BottomSheet + // 바텀 시트 높이 + var bottomHeight: CGFloat = 620 + + // bottomSheet가 view의 상단에서 떨어진 거리 + private var bottomSheetViewTopConstraint: NSLayoutConstraint! + + // 기존 화면을 흐려지게 만들기 위한 뷰 + private let dimmedBackView: UIView = { + let view = UIView() + view.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.5) + return view + }() + + // 바텀 시트 뷰 + private let bottomSheetView: UIView = { + let view = UIView() + view.backgroundColor = .white + + view.layer.cornerRadius = 27 + view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + view.clipsToBounds = true + + return view + }() + + // MARK: - Callendar + + lazy var dateView: UICalendarView = { + var view = UICalendarView() + view.wantsDateDecorations = true + return view + }() + + var selectedDate: DateComponents? = nil + + // MARK: - Properties + + var purposeTextField: UITextField = { + var textField = UITextField() + let centeredParagraphStyle = NSMutableParagraphStyle() + centeredParagraphStyle.alignment = .center + textField.attributedPlaceholder = NSAttributedString(string: "목표", attributes:[NSAttributedString.Key.foregroundColor : UIColor.black, NSAttributedString.Key.paragraphStyle: centeredParagraphStyle]) + textField.layer.cornerRadius = 10 + textField.layer.borderWidth = 1 + return textField + }() + + var everyDayBtn = UIButton(), threeDaysBtn = UIButton(), fiveDaysBtn = UIButton(), everyWeekBtn = UIButton() + + var checkedEverDay = false, checkedThreeDays = false, checkedFiveDays = false, checkedEverWeek = false + + var sundayBtn = UIButton() ,mondayBtn = UIButton(), tuesdayBtn = UIButton(), wednesdayBtn = UIButton(), thursdayBtn = UIButton(), fridayBtn = UIButton(), saturdayBtn = UIButton() + + var checkedSunday = false, checkedMonday = false, checkedTuesday = false, checkedWednesday = false, checkedThursday = false, checkedFridaay = false, checkedSaturday = false + + var saveBtn: UIButton = { + var btn = UIButton() + btn.setTitle("저장", for: .normal) + btn.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20) + btn.setTitleColor(.black, for: .normal) + btn.layer.cornerRadius = 10 + btn.layer.borderWidth = 1 + return btn + }() + + // dismiss Indicator View UI 구성 부분 + private let dismissIndicatorView: UIView = { + let view = UIView() + view.backgroundColor = .systemGray + view.layer.cornerRadius = 3 + return view + }() + + // MARK: - StackView + + var totalStackView = { + var view = UIStackView() + view.axis = .vertical + view.spacing = 10 + view.snp.makeConstraints{ + $0.height.equalTo(120) + } + return view + }() + + lazy var intervalStackView = { + var view = UIStackView(arrangedSubviews: [self.everyDayBtn, self.threeDaysBtn, self.fiveDaysBtn, self.everyWeekBtn]) + view.axis = .horizontal + view.distribution = .equalSpacing + view.snp.makeConstraints{ + $0.height.equalTo(50) + } + return view + }() + + lazy var dayStackView = { + let view = UIStackView(arrangedSubviews: [self.sundayBtn,self.mondayBtn,self.tuesdayBtn,self.wednesdayBtn,self.thursdayBtn,self.fridayBtn,self.saturdayBtn]) + view.axis = .horizontal + view.distribution = .equalSpacing + view.isHidden = true + view.snp.makeConstraints{ + $0.height.equalTo(40) + } + return view + }() + + // MARK: - View Life Cycle + override func viewDidLoad() { + super.viewDidLoad() + + dateView.delegate = self + let dateSelection = UICalendarSelectionSingleDate(delegate: self) + dateView.selectionBehavior = dateSelection + + configurationView() + setupGestureRecognizer() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + showBottomSheet() + } + + // MARK: - @Functions + func configurationView() { + setupUI() + setupLayout() + setDateView() + setPurposeTitle() + setTotalStackView() + + setEveryDayBtn() + setThreeDaysBtn() + setFiveDaysBtn() + setEveryWeekBtn() + + setSundayBtn() + setMondayBtn() + setTuesdayBtn() + setWednesdayBtn() + setThursdayBtn() + setFridayBtn() + setSaturdayBtn() + + setSaveBtn() + } + + // UI 세팅 작업 + private func setupUI() { + view.addSubview(dimmedBackView) + view.addSubview(bottomSheetView) + view.addSubview(dismissIndicatorView) + dimmedBackView.alpha = 0.0 + } + + // GestureRecognizer 세팅 작업 + private func setupGestureRecognizer() { + // 흐린 부분 탭할 때, 바텀시트를 내리는 TapGesture + let dimmedTap = UITapGestureRecognizer(target: self, action: #selector(dimmedViewTapped(_:))) + dimmedBackView.addGestureRecognizer(dimmedTap) + dimmedBackView.isUserInteractionEnabled = true + + // 스와이프 했을 때, 바텀시트를 내리는 swipeGesture + let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(panGesture)) + swipeGesture.direction = .down + view.addGestureRecognizer(swipeGesture) + } + + // 레이아웃 세팅 + private func setupLayout() { + dimmedBackView.snp.makeConstraints{ + $0.top.equalTo(view.safeAreaLayoutGuide.snp.top) + $0.leading.equalTo(view.safeAreaLayoutGuide.snp.leading) + $0.trailing.equalTo(view.safeAreaLayoutGuide.snp.trailing) + $0.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom) + } + + bottomSheetView.translatesAutoresizingMaskIntoConstraints = false + let topConstant = view.safeAreaInsets.bottom + view.safeAreaLayoutGuide.layoutFrame.height + bottomSheetViewTopConstraint = bottomSheetView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: topConstant) + bottomSheetView.snp.makeConstraints{ + $0.leading.equalTo(view.safeAreaLayoutGuide.snp.leading) + $0.trailing.equalTo(view.safeAreaLayoutGuide.snp.trailing) + $0.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom) + } + bottomSheetViewTopConstraint.isActive = true + + dismissIndicatorView.snp.makeConstraints{ + $0.width.equalTo(102) + $0.height.equalTo(7) + $0.top.equalTo(bottomSheetView.snp.top).inset(12) + $0.centerX.equalTo(bottomSheetView.snp.centerX) + } + } + + func setDateView() { + bottomSheetView.addSubview(dateView) + dateView.snp.makeConstraints{ + $0.top.equalToSuperview().inset(10) + $0.leading.equalToSuperview().inset(10) + $0.trailing.equalToSuperview().inset(10) + } + } + + func reloadDateView(date: Date?) { + if date == nil { return } + let calendar = Calendar.current + dateView.reloadDecorations(forDateComponents: [calendar.dateComponents([.day, .month, .year], from: date!)], animated: true) + } + + func setPurposeTitle() { + bottomSheetView.addSubview(purposeTextField) + purposeTextField.snp.makeConstraints { + $0.top.equalTo(dateView.snp.bottom).inset(-10) + $0.leading.equalToSuperview().inset(10) + $0.trailing.equalToSuperview().inset(10) + $0.height.equalTo(50) + } + } + + func setTotalStackView() { + bottomSheetView.addSubview(totalStackView) + totalStackView.addArrangedSubview(intervalStackView) + totalStackView.addArrangedSubview(dayStackView) + totalStackView.snp.makeConstraints { + $0.top.equalTo(purposeTextField.snp.bottom).inset(-10) + $0.leading.equalToSuperview().inset(10) + $0.trailing.equalToSuperview().inset(10) + } + } + + // MARK: - IntervalBtn + func setEveryDayBtn() { + self.setIntervalBtn("매일", everyDayBtn) + everyDayBtn.addTarget(self, action: #selector(clickedEveryDay), for: .touchUpInside) + } + + func setThreeDaysBtn() { + self.setIntervalBtn("3일", threeDaysBtn) + threeDaysBtn.addTarget(self, action: #selector(clickedThreeDays), for: .touchUpInside) + } + + func setFiveDaysBtn() { + self.setIntervalBtn("5일", fiveDaysBtn) + fiveDaysBtn.addTarget(self, action: #selector(clickedFiveDays), for: .touchUpInside) + } + + func setEveryWeekBtn() { + self.setIntervalBtn("매주", everyWeekBtn) + everyWeekBtn.addTarget(self, action: #selector(clickedEveryWeek), for: .touchUpInside) + } + + // MARK: - DaysBtn + func setSundayBtn() { + self.setDayBtn("일", self.sundayBtn) + sundayBtn.layer.borderColor = UIColor.red.cgColor + sundayBtn.setTitleColor(.red, for: .normal) + sundayBtn.addTarget(self, action: #selector(clickedSunday), for: .touchUpInside) + } + + func setMondayBtn() { + self.setDayBtn("월", self.mondayBtn) + mondayBtn.addTarget(self, action: #selector(clickedMonday), for: .touchUpInside) + } + + func setTuesdayBtn() { + self.setDayBtn("화", self.tuesdayBtn) + tuesdayBtn.addTarget(self, action: #selector(clickedTuesday), for: .touchUpInside) + } + + func setWednesdayBtn() { + self.setDayBtn("수", self.wednesdayBtn) + wednesdayBtn.addTarget(self, action: #selector(clickedWednesday), for: .touchUpInside) + } + + func setThursdayBtn() { + self.setDayBtn("목", self.thursdayBtn) + thursdayBtn.addTarget(self, action: #selector(clickedThursday), for: .touchUpInside) + } + + func setFridayBtn() { + self.setDayBtn("금", self.fridayBtn) + fridayBtn.addTarget(self, action: #selector(clickedFriday), for: .touchUpInside) + } + + func setSaturdayBtn() { + self.setDayBtn("토", self.saturdayBtn) + saturdayBtn.layer.borderColor = UIColor.blue.cgColor + saturdayBtn.setTitleColor(.blue, for: .normal) + saturdayBtn.addTarget(self, action: #selector(clickedSaturday), for: .touchUpInside) + } + + func setSaveBtn() { + bottomSheetView.addSubview(saveBtn) + saveBtn.snp.makeConstraints { + $0.top.equalTo(totalStackView.snp.bottom).inset(-10) + $0.leading.equalToSuperview().inset(10) + $0.trailing.equalToSuperview().inset(10) + $0.bottom.equalToSuperview() + $0.height.equalTo(50) + } + saveBtn.addTarget(self, action: #selector(clickedSaveBtn), for: .touchUpInside) + } + + func setIntervalBtn(_ title: String, _ btn: UIButton) { + btn.setTitle(title, for: .normal) + btn.setTitleColor(.black, for: .normal) + btn.layer.cornerRadius = 10 + btn.layer.borderWidth = 1 + btn.snp.makeConstraints{ + $0.width.equalTo(80) + } + } + + func setDayBtn(_ title: String, _ btn: UIButton) { + btn.setTitle(title, for: .normal) + btn.setTitleColor(.black, for: .normal) + btn.layer.cornerRadius = 10 + btn.layer.borderWidth = 1 + btn.snp.makeConstraints{ + $0.width.equalTo(40) + } + } + + // 바텀 시트 표출 애니메이션 + private func showBottomSheet() { + let safeAreaHeight: CGFloat = view.safeAreaLayoutGuide.layoutFrame.height + let bottomPadding: CGFloat = view.safeAreaInsets.bottom + + bottomSheetViewTopConstraint.constant = (safeAreaHeight + bottomPadding) - bottomHeight + + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: { + self.dimmedBackView.alpha = 0.5 + self.view.layoutIfNeeded() + }, completion: nil) + } + + // 바텀 시트 사라지는 애니메이션 + private func hideBottomSheetAndGoBack() { + let safeAreaHeight = view.safeAreaLayoutGuide.layoutFrame.height + let bottomPadding = view.safeAreaInsets.bottom + bottomSheetViewTopConstraint.constant = safeAreaHeight + bottomPadding + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn, animations: { + self.dimmedBackView.alpha = 0.0 + self.view.layoutIfNeeded() + }) { _ in + if self.presentingViewController != nil { + self.dismiss(animated: false, completion: nil) + } + } + } + + // MARK: - @objc + @objc func clickedEveryDay() { + everyDayBtn.backgroundColor = checkedEverDay ? .white : .black + everyDayBtn.setTitleColor(checkedEverDay ? .black : .white, for: .normal) + checkedEverDay = !checkedEverDay + } + + @objc func clickedThreeDays() { + threeDaysBtn.backgroundColor = checkedThreeDays ? .white : .black + threeDaysBtn.setTitleColor(checkedThreeDays ? .black : .white, for: .normal) + checkedThreeDays = !checkedThreeDays + } + + @objc func clickedFiveDays() { + fiveDaysBtn.backgroundColor = checkedFiveDays ? .white : .black + fiveDaysBtn.setTitleColor(checkedFiveDays ? .black : .white, for: .normal) + checkedFiveDays = !checkedFiveDays + } + + @objc func clickedEveryWeek() { + everyWeekBtn.backgroundColor = checkedEverWeek ? .white : .black + everyWeekBtn.setTitleColor(checkedEverWeek ? .black : .white, for: .normal) + checkedEverWeek = !checkedEverWeek + + dayStackView.isHidden = !dayStackView.isHidden + bottomHeight = (dayStackView.isHidden == false) ? 660 : 620 + showBottomSheet() + } + + @objc func clickedSunday() { + sundayBtn.backgroundColor = checkedSunday ? .white : .red + sundayBtn.setTitleColor(checkedSunday ? .red : .white, for: .normal) + checkedSunday = !checkedSunday + } + + @objc func clickedMonday() { + mondayBtn.backgroundColor = checkedMonday ? .white : .black + mondayBtn.setTitleColor(checkedMonday ? .black : .white, for: .normal) + checkedMonday = !checkedMonday + } + + @objc func clickedTuesday() { + tuesdayBtn.backgroundColor = checkedTuesday ? .white : .black + tuesdayBtn.setTitleColor(checkedTuesday ? .black : .white, for: .normal) + checkedTuesday = !checkedTuesday + } + + @objc func clickedWednesday() { + wednesdayBtn.backgroundColor = checkedWednesday ? .white : .black + wednesdayBtn.setTitleColor(checkedWednesday ? .black : .white, for: .normal) + checkedWednesday = !checkedWednesday + } + + @objc func clickedThursday() { + thursdayBtn.backgroundColor = checkedThursday ? .white : .black + thursdayBtn.setTitleColor(checkedThursday ? .black : .white, for: .normal) + checkedThursday = !checkedThursday + } + + @objc func clickedFriday() { + fridayBtn.backgroundColor = checkedFridaay ? .white : .black + fridayBtn.setTitleColor(checkedFridaay ? .black : .white, for: .normal) + checkedFridaay = !checkedFridaay + } + + @objc func clickedSaturday() { + saturdayBtn.backgroundColor = checkedSaturday ? .white : .blue + saturdayBtn.setTitleColor(checkedSaturday ? .blue : .white, for: .normal) + checkedSaturday = !checkedSaturday + } + + @objc func clickedSaveBtn() { + //save + } + + // UITapGestureRecognizer 연결 함수 부분 + @objc private func dimmedViewTapped(_ tapRecognizer: UITapGestureRecognizer) { + hideBottomSheetAndGoBack() + } + + // UISwipeGestureRecognizer 연결 함수 부분 + @objc func panGesture(_ recognizer: UISwipeGestureRecognizer) { + if recognizer.state == .ended { + switch recognizer.direction { + case .down: + hideBottomSheetAndGoBack() + default: + break + } + } + } +} + +@available(iOS 16.0, *) +extension DiaryCalendarViewController: UICalendarViewDelegate, UICalendarSelectionSingleDateDelegate { + // UICalendarView + func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? { + if let selectedDate = selectedDate, selectedDate == dateComponents { + return .customView { + let label = UILabel() +// label.text = "🐶" + label.textAlignment = .center + return label + } + } + return nil + } + + // 달력에서 날짜 선택했을 경우 + func dateSelection(_ selection: UICalendarSelectionSingleDate, didSelectDate dateComponents: DateComponents?) { + selection.setSelected(dateComponents, animated: true) + selectedDate = dateComponents + reloadDateView(date: Calendar.current.date(from: dateComponents!)) + } +} + +//// btn.addTarget(self, action: #selector(pushCallendar), for: .touchUpInside) +// @objc func pushCallendar() { +// let vc = DiaryCalendarViewController() +// vc.modalPresentationStyle = .overFullScreen +// self.present(vc, animated: false, completion: nil) +// } diff --git a/DiaryApp/Controllers/DiaryCalenderViewController.swift b/DiaryApp/Controllers/DiaryCalenderViewController.swift deleted file mode 100644 index f7a70a2..0000000 --- a/DiaryApp/Controllers/DiaryCalenderViewController.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// DiaryCalenderViewController.swift -// DiaryApp -// -// Created by (^ㅗ^)7 iMac on 2023/08/28. -// - -import UIKit - -class DiaryCalenderViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - } -} diff --git a/DiaryApp/Controllers/DiaryListViewController.swift b/DiaryApp/Controllers/DiaryListViewController.swift index ab85548..92bcb12 100644 --- a/DiaryApp/Controllers/DiaryListViewController.swift +++ b/DiaryApp/Controllers/DiaryListViewController.swift @@ -5,10 +5,97 @@ // Created by (^ㅗ^)7 iMac on 2023/08/28. // +import SnapKit import UIKit class DiaryListViewController: UIViewController { + private let collectionView = CustomCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground + configureCollectionView() + configureLayout() + } + + private let collectionViewFlowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.itemSize = Const.itemSize + layout.minimumLineSpacing = Const.itemSpacing + layout.minimumInteritemSpacing = 0 + return layout + }() + + private func configureCollectionView() { + collectionView.register(DiaryListCell.self, forCellWithReuseIdentifier: DiaryListCell.identifier) + collectionView.delegate = self + collectionView.dataSource = self + + collectionView.isScrollEnabled = true + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = true + collectionView.backgroundColor = .clear + collectionView.clipsToBounds = true + collectionView.isPagingEnabled = false + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.contentInset = Const.collectionViewContentInset + collectionView.decelerationRate = .fast + + collectionView.collectionViewLayout = collectionViewFlowLayout + } + + private func configureLayout() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) + make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-10) + } + } +} + +extension DiaryListViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 10 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DiaryListCell.identifier, for: indexPath) as? DiaryListCell else { return UICollectionViewCell() } + cell.diaryTitleLabel.text = "Hello World" + cell.startDayLabel.text = "2023.08.30" + cell.endDayLabel.text = "2023.09.30" + + cell.backgroundColor = .systemPink + cell.collectionView.backgroundColor = .systemBlue + cell.collectionView.layer.cornerRadius = 10 + cell.layer.cornerRadius = 20 + return cell + } +} + +extension DiaryListViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let vc = ProgressListViewController() + navigationController?.pushViewController(vc, animated: true) + } +} + +extension DiaryListViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let interItemSpacing: CGFloat = 10 + let padding: CGFloat = 10 + let width = (collectionView.bounds.width - interItemSpacing * 3 - padding * 2) + print("### \(width)") + let height = width / 3 + return CGSize(width: width, height: height) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 10 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 10 } } diff --git a/DiaryApp/Controllers/DiaryProgressViewController.swift b/DiaryApp/Controllers/DiaryProgressViewController.swift index c52ed74..502d4e9 100644 --- a/DiaryApp/Controllers/DiaryProgressViewController.swift +++ b/DiaryApp/Controllers/DiaryProgressViewController.swift @@ -5,10 +5,85 @@ // Created by (^ㅗ^)7 iMac on 2023/08/28. // +import SnapKit import UIKit class DiaryProgressViewController: UIViewController { + private let collectionView = CustomCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground + setupUI() + configureCollectionView() + } + + private func setupUI() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.top.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide) + } + } + + private func configureCollectionView() { + collectionView.register(DiaryProgressCell.self, forCellWithReuseIdentifier: DiaryProgressCell.identifier) + collectionView.dataSource = self + collectionView.delegate = self + + collectionView.isScrollEnabled = true + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = true + collectionView.backgroundColor = .clear + collectionView.clipsToBounds = true + collectionView.isPagingEnabled = false + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.contentInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) + collectionView.decelerationRate = .fast + + if let flowlayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout { + flowlayout.estimatedItemSize = .zero + } + } +} + +extension DiaryProgressViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 7 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DiaryProgressCell.identifier, for: indexPath) as? DiaryProgressCell else { return UICollectionViewCell() } + cell.backgroundColor = .clear + cell.circularProgressBarView.createCircularPath(0.3) + cell.percentLabel.text = "\(30)%" + return cell + } +} + +extension DiaryProgressViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let vc = ProgressListViewController() + navigationController?.pushViewController(vc, animated: true) + } +} + +extension DiaryProgressViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let interItemSpacing: CGFloat = 10 + let padding: CGFloat = 10 + let width = (collectionView.bounds.width - interItemSpacing * 2 - padding * 2) / 2 + print("### \(width)") + let height = width + return CGSize(width: width, height: height) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 10 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 10 + } } diff --git a/DiaryApp/Controllers/EditDiaryViewController.swift b/DiaryApp/Controllers/EditDiaryViewController.swift index 119f0bc..1378a6a 100644 --- a/DiaryApp/Controllers/EditDiaryViewController.swift +++ b/DiaryApp/Controllers/EditDiaryViewController.swift @@ -10,5 +10,6 @@ import UIKit class EditDiaryViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground } } diff --git a/DiaryApp/Controllers/HomeViewController.swift b/DiaryApp/Controllers/HomeViewController.swift index 06a56c7..1d1bd2f 100644 --- a/DiaryApp/Controllers/HomeViewController.swift +++ b/DiaryApp/Controllers/HomeViewController.swift @@ -5,10 +5,103 @@ // Created by (^ㅗ^)7 iMac on 2023/08/28. // +import SnapKit import UIKit +enum Const { + static let itemSize = CGSize(width: 300, height: 550) + static let itemSpacing = 46.0 + + static var insetX: CGFloat { + (UIScreen.main.bounds.width - Self.itemSize.width) / 2.0 + } + + static var collectionViewContentInset: UIEdgeInsets { + UIEdgeInsets(top: 0, left: Self.insetX, bottom: 0, right: Self.insetX) + } +} + class HomeViewController: UIViewController { + private let collectionView = CustomCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground + configureCollectionView() + configureLayout() + addSearchBar() + } + + private let collectionViewFlowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = Const.itemSize + layout.minimumLineSpacing = Const.itemSpacing + layout.minimumInteritemSpacing = 0 + return layout + }() + + private func configureCollectionView() { + collectionView.register(HomeFeedCell.self, forCellWithReuseIdentifier: HomeFeedCell.identifier) + collectionView.delegate = self + collectionView.dataSource = self + + collectionView.isScrollEnabled = true + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = true + collectionView.backgroundColor = .clear + collectionView.clipsToBounds = true + collectionView.register(HomeFeedCell.self, forCellWithReuseIdentifier: HomeFeedCell.identifier) + collectionView.isPagingEnabled = false + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.contentInset = Const.collectionViewContentInset + collectionView.decelerationRate = .fast + + collectionView.collectionViewLayout = collectionViewFlowLayout + } + + private func configureLayout() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) + make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-20) + } + } + + private func addSearchBar() { + let searchController = UISearchController(searchResultsController: nil) + searchController.hidesNavigationBarDuringPresentation = false + searchController.searchBar.placeholder = "Search" + searchController.navigationItem.hidesSearchBarWhenScrolling = true + navigationItem.searchController = searchController + } +} + +extension HomeViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 3 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: HomeFeedCell.identifier, for: indexPath) as? HomeFeedCell else { return UICollectionViewCell() } + cell.backgroundColor = .systemOrange + cell.layer.cornerRadius = 20 + return cell + } +} + +extension HomeViewController: UICollectionViewDelegate {} + +extension HomeViewController: UICollectionViewDelegateFlowLayout { + func scrollViewWillEndDragging( + _ scrollView: UIScrollView, + withVelocity velocity: CGPoint, + targetContentOffset: UnsafeMutablePointer + ) { + let scrolledOffsetX = targetContentOffset.pointee.x + scrollView.contentInset.left + let cellWidth = Const.itemSize.width + Const.itemSpacing + let index = round(scrolledOffsetX / cellWidth) + targetContentOffset.pointee = CGPoint(x: index * cellWidth - scrollView.contentInset.left, y: scrollView.contentInset.top) } } diff --git a/DiaryApp/Controllers/NewDiaryViewController.swift b/DiaryApp/Controllers/NewDiaryViewController.swift index a22f69d..9e5ef18 100644 --- a/DiaryApp/Controllers/NewDiaryViewController.swift +++ b/DiaryApp/Controllers/NewDiaryViewController.swift @@ -5,10 +5,262 @@ // Created by (^ㅗ^)7 iMac on 2023/08/28. // +import SnapKit import UIKit +import Alamofire class NewDiaryViewController: UIViewController { + let options = ["운동", "식단", "헬스", "크로스핏", "양배추", "닭가슴살", "닭안심살"] + var selectedButton: UIButton? + lazy var pickerView: UIPickerView = { + let pickerView = UIPickerView() + pickerView.delegate = self + pickerView.dataSource = self + return pickerView + }() + + lazy var pickImageButton: UIButton = { + let button = UIButton(type: .system) + button.setImage(UIImage(systemName: "camera"), for: .normal) + button.addTarget(self, action: #selector(pickImage), for: .touchUpInside) + return button + }() + + lazy var textView: UITextView = { + let textView = UITextView() + textView.text = "입력하시요" + textView.layer.borderWidth = 1.0 + textView.layer.borderColor = UIColor.lightGray.cgColor + textView.layer.cornerRadius = 5.0 + return textView + }() + + lazy var button1: UIButton = { + let button = UIButton(type: .system) + button.setTitle("Button 1", for: .normal) + button.addTarget(self, action: #selector(button1Tapped), for: .touchUpInside) + return button + }() + + lazy var button2: UIButton = { + let button = UIButton(type: .system) + button.setTitle("운동", for: .normal) + button.addTarget(self, action: #selector(button1Tapped), for: .touchUpInside) + return button + }() + + lazy var button3: UIButton = { + let button = UIButton(type: .system) + button.setTitle("건강", for: .normal) + button.addTarget(self, action: #selector(button1Tapped), for: .touchUpInside) + return button + }() + + lazy var button4: UIButton = { + let button = UIButton(type: .system) + button.setTitle("식단", for: .normal) + button.addTarget(self, action: #selector(button1Tapped), for: .touchUpInside) + return button + }() + + let weatherIconImageView: UIImageView = { + let imageView = UIImageView() + + return imageView + }() + + let weatherDescriptionLabel: UILabel = { + let label = UILabel() + return label + }() + + let temperatureLabel: UILabel = { + let label = UILabel() + return label + }() + + override func viewDidLoad() { super.viewDidLoad() + + view.backgroundColor = .white + view.addSubview(pickImageButton) + view.addSubview(textView) + view.addSubview(button1) + view.addSubview(button2) + view.addSubview(button3) + view.addSubview(button4) + view.addSubview(weatherIconImageView) + view.addSubview(weatherDescriptionLabel) + view.addSubview(temperatureLabel) + + fetchAndDisplayWeather() + + + pickImageButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + // make.centerY.equalToSuperview() + } + textView.snp.makeConstraints { make in + make.top.equalTo(pickImageButton.snp.bottom).offset(100) + make.leading.trailing.equalToSuperview().inset(20) + make.bottom.equalTo(view.safeAreaLayoutGuide).inset(200) + make.height.equalTo(button1).offset(100) + } + button1.snp.makeConstraints { make in + make.bottom.equalTo(textView.snp.bottom).offset(20) + make.trailing.equalTo(textView).inset(20) + make.top.equalTo(textView).offset(150) + } + button2.snp.makeConstraints { make in + make.top.equalTo(button1.snp.bottom).offset(20) + make.leading.equalTo(button1) + } + button3.snp.makeConstraints { make in + make.top.equalTo(button1.snp.bottom).offset(20) + make.leading.equalTo(button2.snp.leading).offset(-100) + } + button4.snp.makeConstraints { make in + make.top.equalTo(button1.snp.bottom).offset(20) + make.leading.equalTo(button2.snp.leading).offset(-200) + } + weatherIconImageView.snp.makeConstraints { make in +// make.centerX.equalToSuperview() + make.top.equalTo(button4.snp.bottom).offset(20) + make.leading.equalTo(button1) + make.width.height.equalTo(50) + + } + + weatherDescriptionLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(button4.snp.bottom).offset(10) + } + + temperatureLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(weatherDescriptionLabel.snp.bottom).offset(5) + } + } + + @objc func button1Tapped() { + presentAlertWithPicker(for: button1) + } + + @objc func button2Tapped() { + presentAlertWithPicker(for: button2) + } + + @objc func button3Tapped() { + presentAlertWithPicker(for: button3) + } + + @objc func button4Tapped() { + presentAlertWithPicker(for: button4) + } + + + func presentAlertWithPicker(for button: UIButton) { + selectedButton = button + let alertController = UIAlertController(title: "Select", message: nil, preferredStyle: .alert) + alertController.addTextField { textField in + textField.inputView = self.pickerView + } + let confirmAction = UIAlertAction(title: "수정", style: .default) { [weak self] _ in + guard let self = self else { return } + let textField = alertController.textFields?.first + if textField?.inputView == self.pickerView { + let selectedRow = self.pickerView.selectedRow(inComponent: 0) + let selectedOption = self.options[selectedRow] + button.setTitle(selectedOption, for: .normal) + } else { + // Handle custom input from text field + if let customInput = textField?.text, !customInput.isEmpty { + button.setTitle(customInput, for: .normal) + } + } + self.selectedButton = nil + } + let cancelAction = UIAlertAction(title: "취소", style: .cancel) { _ in + self.selectedButton = nil + } + alertController.addAction(confirmAction) + alertController.addAction(cancelAction) + present(alertController, animated: true, completion: nil) + } + func fetchAndDisplayWeather() { + let city = "Busan" + WeatherManager.shared.fetchWeather(for: city) { result in + switch result { + case .success(let weatherData): + // 날씨 정보를 받아와 UI에 표시 + DispatchQueue.main.async { + self.weatherDescriptionLabel.text = weatherData.description + let celsiusTemp = round(weatherData.temperature - 273.15) + + self.temperatureLabel.text = "\(celsiusTemp) °C" + + // 아이콘 이미지를 표시 + if let iconUrl = URL(string: weatherData.iconUrl) { + DispatchQueue.global().async { + + if let imageData = try? Data(contentsOf: iconUrl), + let iconImage = UIImage(data: imageData) { + DispatchQueue.main.async { + print("###이미지 다운로드 완ㄹ") + + self.weatherIconImageView.image = iconImage + } + } else { + print("이미지 다운로드 변환 실패 ###") + } + } + } + } + case .failure(let error): + print("###날씨 정보 가져오기 실패: \(error)") + } + } + } +} + +extension NewDiaryViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + @objc func pickImage() { + showImagePicker() + } + + func showImagePicker() { + let imagePicker = UIImagePickerController() + imagePicker.delegate = self + imagePicker.sourceType = .photoLibrary + present(imagePicker, animated: true, completion: nil) + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + picker.dismiss(animated: true, completion: nil) + if let selectedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage { + let imageView = UIImageView(image: selectedImage) + imageView.contentMode = .scaleAspectFit + view.addSubview(imageView) + imageView.snp.makeConstraints { make in + make.centerX.equalTo(pickImageButton) + make.centerY.equalTo(pickImageButton) + make.width.height.equalTo(300) + } + } + } +} + +extension NewDiaryViewController: UIPickerViewDelegate, UIPickerViewDataSource { + func numberOfComponents(in pickerView: UIPickerView) -> Int { + return 1 + } + + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return options.count + } + + func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + return options[row] } } diff --git a/DiaryApp/Controllers/ProfileViewController.swift b/DiaryApp/Controllers/ProfileViewController.swift index 103969e..1add78a 100644 --- a/DiaryApp/Controllers/ProfileViewController.swift +++ b/DiaryApp/Controllers/ProfileViewController.swift @@ -10,5 +10,6 @@ import UIKit class ProfileViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground } } diff --git a/DiaryApp/Controllers/ProgressListViewController.swift b/DiaryApp/Controllers/ProgressListViewController.swift index dec3de7..fbf6fb3 100644 --- a/DiaryApp/Controllers/ProgressListViewController.swift +++ b/DiaryApp/Controllers/ProgressListViewController.swift @@ -5,10 +5,88 @@ // Created by (^ㅗ^)7 iMac on 2023/08/28. // +import SnapKit import UIKit class ProgressListViewController: UIViewController { + private let collectionView = CustomCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .systemBackground + configureCollectionView() + configureLayout() + } + + private let collectionViewFlowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.itemSize = Const.itemSize + layout.minimumLineSpacing = Const.itemSpacing + layout.minimumInteritemSpacing = 0 + return layout + }() + + private func configureCollectionView() { + collectionView.register(ProgressListCell.self, forCellWithReuseIdentifier: ProgressListCell.identifier) + collectionView.dataSource = self + collectionView.delegate = self + + collectionView.isScrollEnabled = true + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = true + collectionView.backgroundColor = .clear + collectionView.clipsToBounds = true + collectionView.isPagingEnabled = false + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.contentInset = Const.collectionViewContentInset + collectionView.decelerationRate = .fast + + collectionView.collectionViewLayout = collectionViewFlowLayout + } + + private func configureLayout() { + view.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) + make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-10) + } + } +} + +extension ProgressListViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 7 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ProgressListCell.identifier, for: indexPath) as? ProgressListCell else { return UICollectionViewCell() } + cell.imageView.image = UIImage(systemName: "bell") + cell.weatherImageView.image = UIImage(systemName: "sun.max") + cell.descriptionLabel.text = "오늘 하루 운동을 했는데 어쩌구 저쩌구 닭가슴살을 먹었는데 어쩌구 저쩌구 헬스장을 다녀왔는데 어쩌구 저꺼구" + + cell.backgroundColor = .systemRed + cell.layer.cornerRadius = 20 + return cell + } +} + +extension ProgressListViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let interItemSpacing: CGFloat = 10 + let padding: CGFloat = 10 + let width = (collectionView.bounds.width - interItemSpacing * 3 - padding * 2) + print("### \(width)") + let height = width / 3 + return CGSize(width: width, height: height) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 10 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 10 } } diff --git a/DiaryApp/Controllers/Weather/WeatherManager.swift b/DiaryApp/Controllers/Weather/WeatherManager.swift new file mode 100644 index 0000000..71e05e4 --- /dev/null +++ b/DiaryApp/Controllers/Weather/WeatherManager.swift @@ -0,0 +1,61 @@ +// +// WeatherManager.swift +// DiaryApp +// +// Created by 김기현 on 2023/09/03. +// + +import Foundation +import Alamofire + +class WeatherManager { + static let shared = WeatherManager() + + private init() {} + + private let apiKey = "e8aeafe9abc4ec56d53a28782b1991f8" + private let baseUrl = "https://api.openweathermap.org/data/2.5/weather" // HTTPS 사용 + + func fetchWeather(for city: String, completion: @escaping (Result) -> Void) { + let parameters: [String: Any] = [ + "q": city, + "APPID": apiKey + ] + + // Alamofire를 사용하여 GET 요청을 보냅니다. + AF.request(baseUrl, method: .get, parameters: parameters) + .validate() + .responseJSON { response in + switch response.result { + case .success(let value): + if let weatherResponse = WeatherResponse(json: value) { + // 이미지 데이터 가져오기 + self.fetchWeatherIcon(for: weatherResponse.iconUrl) { imageData in + var weatherResponseWithIcon = weatherResponse + weatherResponseWithIcon.iconImageData = imageData + completion(.success(weatherResponseWithIcon)) + } + } else { + print("###날씨 데이터 파싱 실패ㅜㅜ") + } + case .failure(let error): + completion(.failure(error)) + } + } + } + + private func fetchWeatherIcon(for iconUrl: String, completion: @escaping (Data?) -> Void) { + if let url = URL(string: iconUrl) { + AF.request(url).responseData { response in + switch response.result { + case .success(let data): + completion(data) + case .failure(_): + completion(nil) + } + } + } else { + completion(nil) + } + } +} diff --git a/DiaryApp/Controllers/Weather/WeatherResponse.swift b/DiaryApp/Controllers/Weather/WeatherResponse.swift new file mode 100644 index 0000000..fc62465 --- /dev/null +++ b/DiaryApp/Controllers/Weather/WeatherResponse.swift @@ -0,0 +1,24 @@ +import Foundation + +struct WeatherResponse { + let description: String + let temperature: Double + let iconUrl: String + var iconImageData: Data? // 이미지 데이터 추가 + + init?(json: Any) { + guard let jsonDict = json as? [String: Any], + let weatherArray = jsonDict["weather"] as? [[String: Any]], + let mainDict = jsonDict["main"] as? [String: Any], + let description = weatherArray.first?["description"] as? String, + let icon = weatherArray.first?["icon"] as? String, + let temperature = mainDict["temp"] as? Double + else { + return nil + } + + self.description = description + self.temperature = temperature + self.iconUrl = "https://openweathermap.org/img/wn/\(icon)@2x.png" + } +} diff --git a/DiaryApp/Controllers/weatherAPI.plist b/DiaryApp/Controllers/weatherAPI.plist new file mode 100644 index 0000000..7ae8c99 --- /dev/null +++ b/DiaryApp/Controllers/weatherAPI.plist @@ -0,0 +1,8 @@ + + + + + OpenWeather + e8aeafe9abc4ec56d53a28782b1991f8 + + diff --git a/DiaryApp/GoogleService-Info.plist b/DiaryApp/GoogleService-Info.plist new file mode 100644 index 0000000..128a083 --- /dev/null +++ b/DiaryApp/GoogleService-Info.plist @@ -0,0 +1,34 @@ + + + + + CLIENT_ID + 362176913792-tda207b6l1e87ifhcll7sm23qt1f1v7c.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.362176913792-tda207b6l1e87ifhcll7sm23qt1f1v7c + API_KEY + AIzaSyBCu5uLGUJ_eru0wsQOJNulDzFskaFLHOM + GCM_SENDER_ID + 362176913792 + PLIST_VERSION + 1 + BUNDLE_ID + com.Junwoo.Momo8282.DiaryApp + PROJECT_ID + diaryapp-90713 + STORAGE_BUCKET + diaryapp-90713.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:362176913792:ios:523d7a190b449e2d006165 + + \ No newline at end of file diff --git a/DiaryApp/Models/Post.swift b/DiaryApp/Models/Post.swift new file mode 100644 index 0000000..4d9720c --- /dev/null +++ b/DiaryApp/Models/Post.swift @@ -0,0 +1,28 @@ +// +// Post.swift +// DiaryApp +// +// Created by t2023-m0056 on 2023/09/01. +// + +import Foundation + +struct Post: Codable { + var content: String? + var goal: String? + var image: String? + var tag: Array + var temperature: String + var weather: String + var weatherIcon: String + + enum CodingKeys: String, CodingKey { + case content + case goal + case image + case tag + case temperature + case weather + case weatherIcon + } +} diff --git a/DiaryApp/Network/FirestoreManager.swift b/DiaryApp/Network/FirestoreManager.swift new file mode 100644 index 0000000..5e6060e --- /dev/null +++ b/DiaryApp/Network/FirestoreManager.swift @@ -0,0 +1,71 @@ +// +// FirestoreManager.swift +// DiaryApp +// +// Created by t2023-m0056 on 2023/09/01. +// + +import Foundation +import FirebaseFirestore + +final class FirestoreService { + let db = Firestore.firestore() + + func getPostData(completion: @escaping ([Post]?) -> Void) { + var names: [[String:Any]] = [[:]] + var post: [Post]? + + db.collection("Post").getDocuments { (querySnapshot, error) in + if let error = error { + print("Error getting documents: \(error)") + completion(post) // 호출하는 쪽에 빈 배열 전달 + return + } + for document in querySnapshot!.documents { + names.append(document.data()) + } + names.remove(at: 0) + post = self.dictionaryToObject(objectType: Post.self, dictionary: names) + completion(post) // 성공 시 이름 배열 전달 + } + } + + func addPostDocument(content: String, goal: String, image: String, tag: Array, temperature: String, weather: String, weatherIcon: String, completion: @escaping (String) -> Void) { + // Add a new document with a generated ID + db.collection("Info").document(goal).setData([ + "content": content, + "goal": goal, + "image": image, + "tag": tag, + "temperature": temperature, + "weather": weather, + "weatherIcon": weatherIcon, + ]) { err in + if let err = err { + print("Error adding document: \(err)") + } else { + completion("Post Document added") + } + } + } +} + +extension FirestoreService { + func dictionaryToObject(objectType:T.Type,dictionary:[[String:Any]]) -> [T]? { + + guard let dictionaries = try? JSONSerialization.data(withJSONObject: dictionary) else { return nil } + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + guard let objects = try? decoder.decode([T].self, from: dictionaries) else { return nil } + return objects + } + + func dicToObject(objectType:T.Type,dictionary:[String:Any]) -> T? { + + guard let dictionaries = try? JSONSerialization.data(withJSONObject: dictionary) else { return nil } + let decoder = JSONDecoder() + decoder.keyDecodingStrategy = .convertFromSnakeCase + guard let objects = try? decoder.decode(T.self, from: dictionaries) else { return nil } + return objects + } +} diff --git a/DiaryApp/Resources/AppDelegate.swift b/DiaryApp/Resources/AppDelegate.swift index 580f229..f86fe25 100644 --- a/DiaryApp/Resources/AppDelegate.swift +++ b/DiaryApp/Resources/AppDelegate.swift @@ -5,11 +5,13 @@ // Created by (^ㅗ^)7 iMac on 2023/08/28. // +import FirebaseCore import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + FirebaseApp.configure() return true } diff --git a/DiaryApp/Views/Base.lproj/LaunchScreen.storyboard b/DiaryApp/Resources/LaunchScreen.storyboard similarity index 100% rename from DiaryApp/Views/Base.lproj/LaunchScreen.storyboard rename to DiaryApp/Resources/LaunchScreen.storyboard diff --git a/DiaryApp/Resources/SceneDelegate.swift b/DiaryApp/Resources/SceneDelegate.swift index a775bd1..e86883f 100644 --- a/DiaryApp/Resources/SceneDelegate.swift +++ b/DiaryApp/Resources/SceneDelegate.swift @@ -9,41 +9,44 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) window?.makeKeyAndVisible() - + let tabBarController = TabBarController() - + let tabs: [(root: UIViewController, icon: String)] = [ (HomeViewController(), "house"), (DiaryListViewController(), "list.bullet"), - (DiaryCalenderViewController(), "plus.square"), - (DiaryProgressViewController(), "tag.fill"), - (ProfileViewController(), "person.fill"), + (NewDiaryViewController(), "plus.square"), + (DiaryProgressViewController(), "tag"), + (ProfileViewController(), "person"), + (DiaryProgressViewController(), "tag"), + (ProfileViewController(), "person"), ] - + + tabBarController.setViewControllers(tabs.map { root, icon in let navigationController = UINavigationController(rootViewController: root) let tabBarItem = UITabBarItem(title: nil, image: .init(systemName: icon), selectedImage: .init(systemName: "\(icon).fill")) navigationController.tabBarItem = tabBarItem return navigationController }, animated: false) - + tabBarController.tabBar.tintColor = .label tabBarController.tabBar.backgroundColor = .systemBackground window?.rootViewController = tabBarController } - + func sceneDidDisconnect(_ scene: UIScene) {} - + func sceneDidBecomeActive(_ scene: UIScene) {} - + func sceneWillResignActive(_ scene: UIScene) {} - + func sceneWillEnterForeground(_ scene: UIScene) {} - + func sceneDidEnterBackground(_ scene: UIScene) {} } diff --git a/DiaryApp/Views/Cells/DiaryListCell.swift b/DiaryApp/Views/Cells/DiaryListCell.swift new file mode 100644 index 0000000..81fbbd4 --- /dev/null +++ b/DiaryApp/Views/Cells/DiaryListCell.swift @@ -0,0 +1,143 @@ +// +// DiaryListCell.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/29. +// + +import SnapKit +import UIKit + +class DiaryListCell: UICollectionViewCell { + static let identifier = "DiaryListCell" + + let collectionView = CustomCollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) + private let collectionViewFlowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width: 40, height: 40) + layout.minimumLineSpacing = 5 + layout.minimumInteritemSpacing = 2 + layout.estimatedItemSize = .zero + return layout + }() + + private let stackView = CustomStackView(frame: .zero) + private let labelStackView = CustomStackView(frame: .zero) + + lazy var diaryTitleLabel = CustomLabel(frame: .zero) + lazy var startDayLabel = CustomLabel(frame: .zero) + lazy var endDayLabel = CustomLabel(frame: .zero) + private let spaceLabel = CustomLabel(frame: .zero) + + lazy var progressView: UIProgressView = { + let view = UIProgressView() + view.translatesAutoresizingMaskIntoConstraints = false + /// progress 배경 색상 + view.trackTintColor = .lightGray + /// progress 진행 색상 + view.progressTintColor = .systemBlue + view.progress = 0.5 + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + setupLayout() + configureCollectionView() + } + + private func setupUI() { + stackView.axis = .vertical + stackView.backgroundColor = .systemOrange + stackView.distribution = .fillProportionally + stackView.spacing = 0 + + labelStackView.axis = .horizontal + labelStackView.backgroundColor = .systemBlue + labelStackView.distribution = .fillProportionally + + diaryTitleLabel.font = .systemFont(ofSize: 20, weight: .bold) + + startDayLabel.adjustsFontSizeToFitWidth = true + + spaceLabel.text = " ~ " + + endDayLabel.adjustsFontSizeToFitWidth = true + endDayLabel.textAlignment = .center + + collectionView.backgroundColor = .systemRed + } + + private func setupLayout() { + contentView.addSubview(collectionView) + + collectionView.snp.makeConstraints { make in + make.top.equalTo(contentView.safeAreaLayoutGuide).inset(12) + make.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(10) + make.leading.equalTo(contentView.safeAreaLayoutGuide).inset(25) + make.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(210) + make.width.height.equalTo(50) + } + + contentView.addSubview(stackView) + stackView.snp.makeConstraints { make in + make.top.trailing.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(20) + make.leading.equalTo(collectionView.snp.trailing).offset(10) + } + + [diaryTitleLabel, progressView, labelStackView].forEach { + stackView.addArrangedSubview($0) + } + + progressView.snp.makeConstraints { make in + make.bottom.equalTo(labelStackView.snp.top).offset(-5) + make.height.equalTo(15) + } + + labelStackView.snp.makeConstraints { make in + make.height.equalTo(20) + } + + [startDayLabel, spaceLabel, endDayLabel].forEach { + labelStackView.addArrangedSubview($0) + } + } + + private func configureCollectionView() { + collectionView.register(DiaryListThumbnailCell.self, forCellWithReuseIdentifier: DiaryListThumbnailCell.identifier) + collectionView.dataSource = self + + collectionView.isScrollEnabled = false + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = true + collectionView.backgroundColor = .clear + collectionView.clipsToBounds = true + collectionView.isPagingEnabled = false + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.contentInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 5) + collectionView.decelerationRate = .fast + + collectionView.collectionViewLayout = collectionViewFlowLayout + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +extension DiaryListCell: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 4 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: DiaryListThumbnailCell.identifier, for: indexPath) as? DiaryListThumbnailCell else { return UICollectionViewCell() } + cell.backgroundColor = .systemGreen + cell.layer.cornerRadius = 5 + cell.thumbNailImage.image = UIImage(systemName: "bell") + return cell + } +} diff --git a/DiaryApp/Views/Cells/DiaryListThumbnailCell.swift b/DiaryApp/Views/Cells/DiaryListThumbnailCell.swift new file mode 100644 index 0000000..1a4c00f --- /dev/null +++ b/DiaryApp/Views/Cells/DiaryListThumbnailCell.swift @@ -0,0 +1,34 @@ +// +// DiaryListThumbnailCell.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/30. +// + +import SnapKit +import UIKit + +class DiaryListThumbnailCell: UICollectionViewCell { + static let identifier = "DiaryListThumbnailCell" + + lazy var thumbNailImage = CustomImageView(frame: .zero) + + override init(frame: CGRect) { + super.init(frame: frame) + configure() + } + + private func configure() { + thumbNailImage.contentMode = .scaleToFill + + contentView.addSubview(thumbNailImage) + thumbNailImage.snp.makeConstraints { make in + make.top.leading.trailing.bottom.equalTo(contentView) + } + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/DiaryApp/Views/Cells/DiaryProgressCell.swift b/DiaryApp/Views/Cells/DiaryProgressCell.swift new file mode 100644 index 0000000..21f2699 --- /dev/null +++ b/DiaryApp/Views/Cells/DiaryProgressCell.swift @@ -0,0 +1,43 @@ +// +// DiaryProgressCell.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/30. +// + +import SnapKit +import UIKit + +final class DiaryProgressCell: UICollectionViewCell { + static let identifier = "DiaryProgressCell" + + lazy var circularProgressBarView = CircleProgressBar(frame: .zero) + lazy var percentLabel = CustomLabel(frame: .zero) + + override init(frame: CGRect) { + super.init(frame: frame) + setUpCircularProgressBarView() + } + + func setUpCircularProgressBarView() { + circularProgressBarView.translatesAutoresizingMaskIntoConstraints = false + + circularProgressBarView.center = contentView.center + percentLabel.text = "100%" + + contentView.addSubview(circularProgressBarView) + circularProgressBarView.snp.makeConstraints { make in + make.centerX.centerY.equalTo(contentView) + } + + circularProgressBarView.addSubview(percentLabel) + percentLabel.snp.makeConstraints { make in + make.centerX.centerY.equalTo(circularProgressBarView) + } + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/DiaryApp/Views/Cells/HomeFeedCell.swift b/DiaryApp/Views/Cells/HomeFeedCell.swift new file mode 100644 index 0000000..248bd37 --- /dev/null +++ b/DiaryApp/Views/Cells/HomeFeedCell.swift @@ -0,0 +1,42 @@ +// +// HomeFeedCell.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/28. +// + +import SnapKit +import UIKit + +final class HomeFeedCell: UICollectionViewCell { + static let identifier = "HomeFeedCell" + + private let myView = CustomImageView(frame: .zero) + private let weatherImage = CustomImageView(frame: .zero) + + let sizeWidth = 70.0 + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override init(frame: CGRect) { + super.init(frame: frame) + + self.contentView.addSubview(self.myView) + + self.weatherImage.backgroundColor = .systemBlue + self.weatherImage.layer.cornerRadius = CGFloat(self.sizeWidth / 2) + + self.myView.addSubview(self.weatherImage) + self.myView.snp.makeConstraints { make in + make.top.bottom.leading.trailing.equalTo(contentView) + } + + self.weatherImage.snp.makeConstraints { make in + make.trailing.bottom.equalTo(self.myView).offset(-30) + make.height.width.equalTo(self.sizeWidth) + } + } +} diff --git a/DiaryApp/Views/Cells/ProgressListCell.swift b/DiaryApp/Views/Cells/ProgressListCell.swift new file mode 100644 index 0000000..e95c440 --- /dev/null +++ b/DiaryApp/Views/Cells/ProgressListCell.swift @@ -0,0 +1,119 @@ +// +// ProgressListCell.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/31. +// + +import SnapKit +import UIKit + +class ProgressListCell: UICollectionViewCell { + static let identifier = "ProgressListCell" + + lazy var imageView = CustomImageView(frame: .zero) + lazy var weatherImageView = CustomImageView(frame: .zero) + lazy var descriptionLabel = CustomLabel(frame: .zero) + + private let collectionView = CustomCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) + + private let collectionViewFlowLayout: UICollectionViewFlowLayout = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width: 50, height: 20) + layout.minimumLineSpacing = 5 + layout.minimumInteritemSpacing = 2 + layout.estimatedItemSize = .zero + return layout + }() + + override init(frame: CGRect) { + super.init(frame: frame) + configureCell() + setupLayout() + configureCollectionView() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func configureCell() { + imageView.contentMode = .scaleAspectFit + imageView.layer.cornerRadius = 20 + imageView.backgroundColor = .systemBrown + + descriptionLabel.backgroundColor = .white + descriptionLabel.font = .systemFont(ofSize: 15, weight: .regular) + descriptionLabel.numberOfLines = 3 + descriptionLabel.textAlignment = .left + descriptionLabel.adjustsFontSizeToFitWidth = false + + weatherImageView.contentMode = .scaleAspectFit + weatherImageView.layer.cornerRadius = weatherImageView.bounds.size.width / 2 + weatherImageView.backgroundColor = .purple + } + + private func setupLayout() { + contentView.addSubview(imageView) + imageView.snp.makeConstraints { make in + make.top.leading.bottom.equalTo(contentView) + make.width.equalTo(imageView.snp.height) + } + + contentView.addSubview(weatherImageView) + weatherImageView.snp.makeConstraints { make in + make.leading.equalTo(imageView.snp.trailing).offset(10) + make.top.equalTo(contentView).inset(5) + make.width.height.equalTo(30) + } + + contentView.addSubview(descriptionLabel) + descriptionLabel.snp.makeConstraints { make in + make.leading.equalTo(imageView.snp.trailing).offset(10) + make.trailing.equalTo(contentView).inset(10) + make.top.equalTo(weatherImageView.snp.bottom).offset(5) + make.bottom.equalTo(contentView).inset(10) + } + + contentView.addSubview(collectionView) + collectionView.snp.makeConstraints { make in + make.top.equalTo(contentView).inset(5) + make.trailing.equalTo(contentView).inset(10) + make.bottom.equalTo(descriptionLabel.snp.top).offset(5) + make.leading.equalTo(weatherImageView.snp.trailing).offset(5) + } + } + + private func configureCollectionView() { + collectionView.register(ProgressTagCell.self, forCellWithReuseIdentifier: ProgressTagCell.identifier) + collectionView.dataSource = self + + collectionView.isScrollEnabled = false + collectionView.showsHorizontalScrollIndicator = false + collectionView.showsVerticalScrollIndicator = true + collectionView.backgroundColor = .clear + collectionView.clipsToBounds = true + collectionView.isPagingEnabled = false + collectionView.contentInsetAdjustmentBehavior = .never + collectionView.contentInset = UIEdgeInsets(top: 5, left: 5, bottom: 15, right: 5) + collectionView.decelerationRate = .fast + + collectionView.collectionViewLayout = collectionViewFlowLayout + } +} + +extension ProgressListCell: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 3 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ProgressTagCell.identifier, for: indexPath) as? ProgressTagCell else { return UICollectionViewCell() } + cell.tagTitleLabel.text = "Hi" + cell.backgroundColor = .systemYellow + cell.layer.cornerRadius = 5 + return cell + } +} diff --git a/DiaryApp/Views/Cells/ProgressTagCell.swift b/DiaryApp/Views/Cells/ProgressTagCell.swift new file mode 100644 index 0000000..c8893bc --- /dev/null +++ b/DiaryApp/Views/Cells/ProgressTagCell.swift @@ -0,0 +1,39 @@ +// +// ProgressTagCell.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/31. +// + +import SnapKit +import UIKit + +final class ProgressTagCell: UICollectionViewCell { + static let identifier = "ProgressTagCell" + + lazy var tagTitleLabel = CustomLabel(frame: .zero) + + override init(frame: CGRect) { + super.init(frame: frame) + setupUI() + setupLayout() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupUI() { + tagTitleLabel.numberOfLines = 1 + tagTitleLabel.font = .systemFont(ofSize: 15) + tagTitleLabel.textAlignment = .center + } + + func setupLayout() { + contentView.addSubview(tagTitleLabel) + tagTitleLabel.snp.makeConstraints { make in + make.top.bottom.leading.trailing.equalTo(contentView).inset(-10) + } + } +} diff --git a/DiaryApp/Views/CircleProgressBar.swift b/DiaryApp/Views/CircleProgressBar.swift new file mode 100644 index 0000000..92043e5 --- /dev/null +++ b/DiaryApp/Views/CircleProgressBar.swift @@ -0,0 +1,62 @@ +// +// CircleProgressBar.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/30. +// + +import SnapKit +import UIKit + +final class CircleProgressBar: UIView { + private var circleLayer = CAShapeLayer() + private var progressLayer = CAShapeLayer() + private var startPoint = CGFloat(-Double.pi / 2) + private var endPoint = CGFloat(3 * Double.pi / 2) + var dayCount: Double? + + override init(frame: CGRect) { + super.init(frame: frame) + createCircularPath(dayCount ?? 0) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func createCircularPath(_ strokeEnd: Double) { + let circularPath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: 65, startAngle: startPoint, endAngle: endPoint, clockwise: true) + // circleLayer path defined to circularPath + circleLayer.path = circularPath.cgPath + // ui edits + circleLayer.fillColor = UIColor.clear.cgColor + circleLayer.lineCap = .round + circleLayer.lineWidth = 20.0 + circleLayer.strokeEnd = 1.0 + circleLayer.strokeColor = UIColor.systemGray6.cgColor + // added circleLayer to layer + layer.addSublayer(circleLayer) + // progressLayer path defined to circularPath + progressLayer.path = circularPath.cgPath + // ui edits + progressLayer.fillColor = UIColor.clear.cgColor + progressLayer.lineCap = .round + progressLayer.lineWidth = 20.0 + progressLayer.strokeEnd = strokeEnd + progressLayer.strokeColor = UIColor.systemBlue.cgColor + // added progressLayer to layer + layer.addSublayer(progressLayer) + } + +// func progressAnimation(duration: TimeInterval) { +// // created circularProgressAnimation with keyPath +// let circularProgressAnimation = CABasicAnimation(keyPath: "strokeEnd") +// // set the end time +// circularProgressAnimation.duration = 30 +// circularProgressAnimation.toValue = 3 +// circularProgressAnimation.fillMode = .forwards +// circularProgressAnimation.isRemovedOnCompletion = false +// progressLayer.add(circularProgressAnimation, forKey: "progressAnim") +// } +} diff --git a/DiaryApp/Views/CustomCollectionView.swift b/DiaryApp/Views/CustomCollectionView.swift new file mode 100644 index 0000000..b74407e --- /dev/null +++ b/DiaryApp/Views/CustomCollectionView.swift @@ -0,0 +1,20 @@ +// +// CustomCollectionView.swift +// CollectionWithoutSB +// +// Created by (^ㅗ^)7 iMac on 2023/08/23. +// + +import UIKit + +final class CustomCollectionView: UICollectionView { + override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { + super.init(frame: frame, collectionViewLayout: layout) + self.translatesAutoresizingMaskIntoConstraints = false + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/DiaryApp/Views/CustomImageView.swift b/DiaryApp/Views/CustomImageView.swift new file mode 100644 index 0000000..6a0f6c3 --- /dev/null +++ b/DiaryApp/Views/CustomImageView.swift @@ -0,0 +1,21 @@ +// +// CustomImageView.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/28. +// + +import UIKit + +final class CustomImageView: UIImageView { + override init(frame: CGRect) { + super.init(frame: frame) + self.image = nil + self.translatesAutoresizingMaskIntoConstraints = false + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/DiaryApp/Views/CustomLabel.swift b/DiaryApp/Views/CustomLabel.swift new file mode 100644 index 0000000..7c09492 --- /dev/null +++ b/DiaryApp/Views/CustomLabel.swift @@ -0,0 +1,21 @@ +// +// CustomLabel.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/29. +// + +import UIKit + +final class CustomLabel: UILabel { + override init(frame: CGRect) { + super.init(frame: frame) + self.textColor = .black + self.translatesAutoresizingMaskIntoConstraints = false + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/DiaryApp/Views/CustomStackView.swift b/DiaryApp/Views/CustomStackView.swift new file mode 100644 index 0000000..b68a694 --- /dev/null +++ b/DiaryApp/Views/CustomStackView.swift @@ -0,0 +1,20 @@ +// +// CustomStackView.swift +// DiaryApp +// +// Created by (^ㅗ^)7 iMac on 2023/08/29. +// + +import UIKit + +final class CustomStackView: UIStackView { + override init(frame: CGRect) { + super.init(frame: frame) + self.translatesAutoresizingMaskIntoConstraints = false + } + + @available(*, unavailable) + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/README.md b/README.md index 4e6a088..d2f8682 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,17 @@ - develop : 개발용 default 브랜치로, 이 브랜치를 기준으로 feature 브랜치를 따고, 각 feature를 합치는 브랜치 - feature: 단위 기능 개발용 브랜치 -> feature/작업할페이지컨트롤러명 -### 참고 -1. 커밋은 기능 단위로!! -> 한 번에 모아서 올리면, 무슨 기능인지 분간이 안됨. -2. 맡은 작업 페이지 외에 다른 작업은 건드리지 말 것!! +### Git Conventions +Commit Message는 아래와 같은 규칙을 따릅니다. +- [FEAT]: 새로운 기능을 추가 +[FIX]: 잔잔바리 수정 +- [DESIGN]: UI 디자인 변경 +- [STYLE]: 코드 포맷 변경, 세미 콜론 누락, 코드 수정이 없는 경우 +- [DOCS]: 문서 수정, 필요한 주석 추가 및 변경 +- [RENAME]: 파일 혹은 폴더명을 수정하거나 옮기는 작업만인 경우 +- [REMOVE]: 파일을 삭제하는 작업만 수행한 경우 +- [MERGE] : 병합 +- [CONFLICT]: 병합 시 충돌 해결 +- [COMPLETION]: 작업을 완료하고 마지막 커밋을 작성하는 경우 +Pull Requests는 Commit Message와 동일하게 써서 올립니다.