diff --git a/HappyAnding/HappyAnding.xcodeproj/project.pbxproj b/HappyAnding/HappyAnding.xcodeproj/project.pbxproj index 8f882886..206637ab 100644 --- a/HappyAnding/HappyAnding.xcodeproj/project.pbxproj +++ b/HappyAnding/HappyAnding.xcodeproj/project.pbxproj @@ -139,6 +139,7 @@ F91F09DF29AE0B5E00E04FA0 /* GradeAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F91F09DE29AE0B5E00E04FA0 /* GradeAlertView.swift */; }; F92766552A61A032009C4EC2 /* WriteShortcutModalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92766542A61A032009C4EC2 /* WriteShortcutModalViewModel.swift */; }; F92766562A61A0E5009C4EC2 /* WriteShortcutModalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F92766542A61A032009C4EC2 /* WriteShortcutModalViewModel.swift */; }; + F930E0002BBD51EC003C2686 /* Seal.swift in Sources */ = {isa = PBXBuildFile; fileRef = F930DFFF2BBD51EC003C2686 /* Seal.swift */; }; F94B432E2907088400987819 /* UserCurationListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F94B432D2907088400987819 /* UserCurationListView.swift */; }; F94B435D2907B19A00987819 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = F94B435C2907B19A00987819 /* FirebaseAnalytics */; }; F94B435F2907B19A00987819 /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = F94B435E2907B19A00987819 /* FirebaseAuth */; }; @@ -150,6 +151,12 @@ F9724BBF292755E400860F8A /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9724BBE292755E400860F8A /* Comment.swift */; }; F976E82C29368E0D0088BBA1 /* Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = F976E82B29368E0D0088BBA1 /* Version.swift */; }; F976E85129395B350088BBA1 /* ShareExtensionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F976E85029395B350088BBA1 /* ShareExtensionViewModel.swift */; }; + F98017182BBC29A7004F2EA7 /* SCZ+Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98017172BBC29A7004F2EA7 /* SCZ+Color.swift */; }; + F980171A2BBC29D6004F2EA7 /* PromotionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98017192BBC29D6004F2EA7 /* PromotionSection.swift */; }; + F980171C2BBC29F7004F2EA7 /* CardSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F980171B2BBC29F7004F2EA7 /* CardSection.swift */; }; + F98017202BBC2A6F004F2EA7 /* ExploreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F980171F2BBC2A6F004F2EA7 /* ExploreCell.swift */; }; + F98017222BBC3FD8004F2EA7 /* ExpandedRankingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98017212BBC3FD8004F2EA7 /* ExpandedRankingView.swift */; }; + F98017242BBC4061004F2EA7 /* ShortcutIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F98017232BBC4061004F2EA7 /* ShortcutIcon.swift */; }; F99569182901DC4D0060AAEF /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F99569172901DC4D0060AAEF /* UIFont+Extension.swift */; }; F9A86DA62A0B54ED00405E12 /* UserNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9A86DA52A0B54ED00405E12 /* UserNameCell.swift */; }; F9AC2BB62935201C00165820 /* CheckUpdateVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9AC2BB52935201C00165820 /* CheckUpdateVersion.swift */; }; @@ -312,6 +319,7 @@ F91F09DC29AE012600E04FA0 /* ShortcutGrade.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutGrade.swift; sourceTree = ""; }; F91F09DE29AE0B5E00E04FA0 /* GradeAlertView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradeAlertView.swift; sourceTree = ""; }; F92766542A61A032009C4EC2 /* WriteShortcutModalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteShortcutModalViewModel.swift; sourceTree = ""; }; + F930DFFF2BBD51EC003C2686 /* Seal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Seal.swift; sourceTree = ""; }; F94B432D2907088400987819 /* UserCurationListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserCurationListView.swift; sourceTree = ""; }; F96D45B62980301F000C2441 /* SubtitleTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubtitleTextView.swift; sourceTree = ""; }; F96D45BA29804057000C2441 /* EnvironmentValues+Alerter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentValues+Alerter.swift"; sourceTree = ""; }; @@ -319,6 +327,12 @@ F9724BBE292755E400860F8A /* Comment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; F976E82B29368E0D0088BBA1 /* Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Version.swift; sourceTree = ""; }; F976E85029395B350088BBA1 /* ShareExtensionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionViewModel.swift; sourceTree = ""; }; + F98017172BBC29A7004F2EA7 /* SCZ+Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SCZ+Color.swift"; sourceTree = ""; }; + F98017192BBC29D6004F2EA7 /* PromotionSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromotionSection.swift; sourceTree = ""; }; + F980171B2BBC29F7004F2EA7 /* CardSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardSection.swift; sourceTree = ""; }; + F980171F2BBC2A6F004F2EA7 /* ExploreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreCell.swift; sourceTree = ""; }; + F98017212BBC3FD8004F2EA7 /* ExpandedRankingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandedRankingView.swift; sourceTree = ""; }; + F98017232BBC4061004F2EA7 /* ShortcutIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutIcon.swift; sourceTree = ""; }; F99569172901DC4D0060AAEF /* UIFont+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Extension.swift"; sourceTree = ""; }; F9A86DA52A0B54ED00405E12 /* UserNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNameCell.swift; sourceTree = ""; }; F9AC2BB52935201C00165820 /* CheckUpdateVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckUpdateVersion.swift; sourceTree = ""; }; @@ -416,13 +430,6 @@ path = ExploreShortcutViewModels; sourceTree = ""; }; - 8788E1A22A484528007C3852 /* WriteShortcutViewModels */ = { - isa = PBXGroup; - children = ( - ); - path = WriteShortcutViewModels; - sourceTree = ""; - }; 8788E1A32A484533007C3852 /* ReadShortcutViewModels */ = { isa = PBXGroup; children = ( @@ -432,20 +439,6 @@ path = ReadShortcutViewModels; sourceTree = ""; }; - 8788E1A42A484542007C3852 /* WriteCurationViewModels */ = { - isa = PBXGroup; - children = ( - ); - path = WriteCurationViewModels; - sourceTree = ""; - }; - 8788E1A52A48456E007C3852 /* MyPageViewModels */ = { - isa = PBXGroup; - children = ( - ); - path = MyPageViewModels; - sourceTree = ""; - }; 87E606AE291062D300C3DA13 /* SignInViews */ = { isa = PBXGroup; children = ( @@ -583,6 +576,7 @@ 4D061BB72A47531800F76835 /* ExploreShortcutView.swift */, 87E99CC8290145B8009B691F /* ListShortcutView.swift */, A0F822B629164D2300AF4448 /* ListCategoryShortcutView.swift */, + F98017212BBC3FD8004F2EA7 /* ExpandedRankingView.swift */, ); path = ExploreShortcutViews; sourceTree = ""; @@ -615,6 +609,7 @@ 8786B2DE29A7F8D1000B46A1 /* View */, 8786B2DD29A7F8C8000B46A1 /* String */, 87E99CC329014572009B691F /* Color+Extension.swift */, + F98017172BBC29A7004F2EA7 /* SCZ+Color.swift */, 8786B2EB29A7FAD2000B46A1 /* UIColor+Extension.swift */, F99569172901DC4D0060AAEF /* UIFont+Extension.swift */, 4D778A33290A53BA00C15AC4 /* UIApplication+Keyboard.swift */, @@ -650,6 +645,11 @@ F91A72C22999160E00CA135A /* Alerter.swift */, F91F09DE29AE0B5E00E04FA0 /* GradeAlertView.swift */, F9A86DA52A0B54ED00405E12 /* UserNameCell.swift */, + F98017192BBC29D6004F2EA7 /* PromotionSection.swift */, + F980171B2BBC29F7004F2EA7 /* CardSection.swift */, + F980171F2BBC2A6F004F2EA7 /* ExploreCell.swift */, + F98017232BBC4061004F2EA7 /* ShortcutIcon.swift */, + F930DFFF2BBD51EC003C2686 /* Seal.swift */, ); path = Components; sourceTree = ""; @@ -669,6 +669,7 @@ F91F09DC29AE012600E04FA0 /* ShortcutGrade.swift */, 872B5D3C2A2E0FF9008DCC57 /* CurationType.swift */, A323D3C92AEE870700DDA716 /* SuggestionForm.swift */, + F980171D2BBC2A24004F2EA7 /* ExploreShortcutSectionType.swift */, ); path = Model; sourceTree = ""; @@ -686,11 +687,8 @@ isa = PBXGroup; children = ( 8788E1A12A484518007C3852 /* ExploreShortcutViewModels */, - 8788E1A22A484528007C3852 /* WriteShortcutViewModels */, 8788E1A32A484533007C3852 /* ReadShortcutViewModels */, 8788E19E2A483FDF007C3852 /* ExploreCurationViewModels */, - 8788E1A42A484542007C3852 /* WriteCurationViewModels */, - 8788E1A52A48456E007C3852 /* MyPageViewModels */, A0F822AB2910B8F100AF4448 /* ShortcutsZipViewModel.swift */, 4D61A766291E1EE8000EF531 /* NavigationViewModel.swift */, F9AC2BB52935201C00165820 /* CheckUpdateVersion.swift */, @@ -978,22 +976,26 @@ A0DD085729276608008177BB /* URL+DeepLink.swift in Sources */, 87E99CD929042536009B691F /* SectionType.swift in Sources */, 872A7D8F2918393B004A05B8 /* PrivacyPolicyView.swift in Sources */, + F980171A2BBC29D6004F2EA7 /* PromotionSection.swift in Sources */, A38115BA292B447D0043E8B8 /* ShortcutCardCell.swift in Sources */, 8792479B291BDF820040D5C3 /* SearchView.swift in Sources */, A3FF018E291ACFA500384211 /* WithdrawalView.swift in Sources */, 4DF15D732A4ECC7D0014F854 /* ListShortcutViewModel.swift in Sources */, 4D778A34290A53BA00C15AC4 /* UIApplication+Keyboard.swift in Sources */, + F98017242BBC4061004F2EA7 /* ShortcutIcon.swift in Sources */, A3766B232904330300708F83 /* ReadCurationView.swift in Sources */, 87E606B0291062F900C3DA13 /* AppleAuthCoordinator.swift in Sources */, F9A86DA62A0B54ED00405E12 /* UserNameCell.swift in Sources */, 8795A170292AB945004B765F /* UIScreen+Size.swift in Sources */, F9AFF6E32A59153B00FFFFAD /* WriteShortcutViewModel.swift in Sources */, 8786B2E629A7F987000B46A1 /* String+Date.swift in Sources */, + F980171C2BBC29F7004F2EA7 /* CardSection.swift in Sources */, F96D45B72980301F000C2441 /* SubtitleTextView.swift in Sources */, A323D3CA2AEE870700DDA716 /* SuggestionForm.swift in Sources */, 4DF15D752A4ECE1F0014F854 /* ListCategoryShortcutViewModel.swift in Sources */, 87E99CDB29042CCA009B691F /* Category.swift in Sources */, 876B4F6F299E3D91009672D9 /* NavigationRouter.swift in Sources */, + F98017202BBC2A6F004F2EA7 /* ExploreCell.swift in Sources */, A04ACB062903D0B2004A85A6 /* MyShortcutCardView.swift in Sources */, 87E99CA728FFF23E009B691F /* ListCurationView.swift in Sources */, F9CAEF832914855900224B0A /* Date+String.swift in Sources */, @@ -1016,10 +1018,12 @@ A309862F2BBFE6B90004D993 /* View+Shadow.swift in Sources */, 87E99CC7290145AD009B691F /* ShortcutCell.swift in Sources */, 87E99CBB28FFF298009B691F /* IconModalView.swift in Sources */, + F98017222BBC3FD8004F2EA7 /* ExpandedRankingView.swift in Sources */, 87E99C7028F94EA6009B691F /* ShortcutTabView.swift in Sources */, F99569182901DC4D0060AAEF /* UIFont+Extension.swift in Sources */, F91A72C32999160E00CA135A /* Alerter.swift in Sources */, 87E99CAD28FFF261009B691F /* ReadShortcutView.swift in Sources */, + F930E0002BBD51EC003C2686 /* Seal.swift in Sources */, 4D5889E82AA36A52000C4849 /* AppDelegate.swift in Sources */, A33F74AE2908D8C800B8D0D0 /* CheckBoxShortcutCell.swift in Sources */, 87E606B22910649B00C3DA13 /* SignInWithAppleView.swift in Sources */, @@ -1043,6 +1047,7 @@ A3FF01862918552E00384211 /* AboutTeamView.swift in Sources */, 87E99C6E28F94EA6009B691F /* HappyAndingApp.swift in Sources */, 87E99CD32901465F009B691F /* ValidationCheckTextField.swift in Sources */, + F98017182BBC29A7004F2EA7 /* SCZ+Color.swift in Sources */, 87DBFB062A2127C0000CC442 /* CheckVersionView.swift in Sources */, A3FF01882918581E00384211 /* LicenseView.swift in Sources */, A3439AFB2939B0E80043E273 /* UserDefaults+Extension.swift in Sources */, diff --git a/HappyAnding/HappyAnding/Assets.xcassets/bannerBg.imageset/Contents.json b/HappyAnding/HappyAnding/Assets.xcassets/bannerBg.imageset/Contents.json new file mode 100644 index 00000000..c3837ed5 --- /dev/null +++ b/HappyAnding/HappyAnding/Assets.xcassets/bannerBg.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "bannerBg.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HappyAnding/HappyAnding/Assets.xcassets/bannerBg.imageset/bannerBg.png b/HappyAnding/HappyAnding/Assets.xcassets/bannerBg.imageset/bannerBg.png new file mode 100644 index 00000000..13fb7af5 Binary files /dev/null and b/HappyAnding/HappyAnding/Assets.xcassets/bannerBg.imageset/bannerBg.png differ diff --git a/HappyAnding/HappyAnding/Extensions/Color+Extension.swift b/HappyAnding/HappyAnding/Extensions/Color+Extension.swift index d1eac0ff..11346065 100644 --- a/HappyAnding/HappyAnding/Extensions/Color+Extension.swift +++ b/HappyAnding/HappyAnding/Extensions/Color+Extension.swift @@ -11,6 +11,21 @@ extension Color { init(light: Color, dark: Color) { self.init(UIColor(light: UIColor(light), dark: UIColor(dark))) } + + init(hexString: String, opacity: Double = 1.0) { + let hex: Int = Int(hexString, radix: 16)! + + let red = Double((hex >> 16) & 0xff) / 255 + let green = Double((hex >> 8) & 0xff) / 255 + let blue = Double((hex >> 0) & 0xff) / 255 + + self.init(.sRGB, red: red, green: green, blue: blue, opacity: opacity) + } + + func toGradient() -> LinearGradient { + return LinearGradient(colors: [self], startPoint: .top, endPoint: .bottom) + } + } extension Color { diff --git a/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift b/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift new file mode 100644 index 00000000..5ed1833b --- /dev/null +++ b/HappyAnding/HappyAnding/Extensions/SCZ+Color.swift @@ -0,0 +1,577 @@ +// +// SCZ+Color.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +/// 단축어 아이콘 셀 배경 색상을 미리 정의해둔 파일입니다. +/// SCZColor.colors[shortcut.color]!.color(for: colorScheme).fillGradient() 와 같이 사용 가능합니다. +/// +protocol ColorProtocol { + var light: ColorType { get } + var dark: ColorType { get } +} + +extension ColorProtocol { + func color(for scheme: ColorScheme) -> ColorType { + switch scheme { + case .light: + return light + case .dark: + return dark + @unknown default: + return light + } + } +} + +struct ColorType { + var fill: GradientType + var stroke: GradientType? + + func fillGradient() -> LinearGradient { + return LinearGradient( + gradient: Gradient(colors: [Color(hexString: fill.gradient01), + Color(hexString: fill.gradient02)] + ), + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + } + + func strokeGradient() -> LinearGradient? { + guard let stroke else { return nil } + return LinearGradient( + gradient: Gradient(colors: [Color(hexString: stroke.gradient01), + Color(hexString: stroke.gradient02)] + ), + startPoint: .top, + endPoint: .bottom + ) + } +} + +struct GradientType { + var gradient01: String + var gradient02: String +} + +struct SCZColor { + static let defaultColor = Red().light.fillGradient() + static let colors: [String: ColorProtocol] = [ + "Red": Red(), + "Coral": Coral(), + "Orange": Orange(), + "Yellow": Yellow(), + "Green": Green(), + "Mint": Mint(), + "Teal": Teal(), + "Cyan": Cyan(), + "Blue": Blue(), + "Purple": Purple(), + "LightPurple": Lavendar(), + "Silver": Silver(), + "Khaki": Khaki(), + "Brown": Brown() + ] + + struct Red: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "F46D71", + gradient02: "F25A5F" + ), + stroke: + GradientType( + gradient01: "F26367", + gradient02: "F05056" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "EF676D", + gradient02: "EC5057" + ), + stroke: nil + ) + } + } + struct Coral: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "FF8C70", + gradient02: "FF7C5E" + ), + stroke: + GradientType( + gradient01: "FF8166", + gradient02: "FF6F53" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "FF8C70", + gradient02: "FF7C5E" + ), + stroke: nil + ) + } + } + struct Orange: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "F8AD5C", + gradient02: "F7A145" + ), + stroke: + GradientType( + gradient01: "F7A453", + gradient02: "F7993E" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "F8AD5C", + gradient02: "F7A145" + ), + stroke: nil + ) + } + } + struct Yellow: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "EEC806", + gradient02: "ECC001" + ), + stroke: + GradientType( + gradient01: "EDC203", + gradient02: "E9BA00" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "EEC806", + gradient02: "ECC001" + ), + stroke: nil + ) + } + } + struct Green: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "4ECD68", + gradient02: "37C554" + ), + stroke: + GradientType( + gradient01: "48C85F", + gradient02: "31BF4B" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "4ECD68", + gradient02: "37C554" + ), + stroke: nil + ) + } + } + struct Mint: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "00D1B1", + gradient02: "00CBA6" + ), + stroke: + GradientType( + gradient01: "00CDAA", + gradient02: "03C59E" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "00D1B1", + gradient02: "00CBA6" + ), + stroke: nil + ) + } + } + struct Teal: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "00CEE0", + gradient02: "00C8DD" + ), + stroke: + GradientType( + gradient01: "04C9DE", + gradient02: "00C2D9" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "00CEE0", + gradient02: "00C8DD" + ), + stroke: + GradientType( + gradient01: "04C9DE", + gradient02: "00C2D9" + ) + ) + } + } + struct Cyan: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "02B9F8", + gradient02: "00B0F6" + ), + stroke: + GradientType( + gradient01: "00B2F6", + gradient02: "01A7F4" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "02B9F8", + gradient02: "00B0F6" + ), + stroke: + GradientType( + gradient01: "00B2F6", + gradient02: "01A7F4" + ) + ) + } + } + struct Blue: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "5572CA", + gradient02: "3F60C3" + ), + stroke: + GradientType( + gradient01: "4E67C5", + gradient02: "3655BC" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "5572CA", + gradient02: "3F60C3" + ), + stroke: + GradientType( + gradient01: "4E67C5", + gradient02: "3655BC" + ) + ) + } + } + struct Purple: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "8F61C6", + gradient02: "804DBF" + ), + stroke: + GradientType( + gradient01: "8458C1", + gradient02: "7343B7" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "8F61C6", + gradient02: "804DBF" + ), + stroke: + GradientType( + gradient01: "8458C1", + gradient02: "7343B7" + ) + ) + } + } + struct Lavendar: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "BF82E6", + gradient02: "B772E2" + ), + stroke: + GradientType( + gradient01: "B876E3", + gradient02: "AE66E0" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "BF82E6", + gradient02: "B772E2" + ), + stroke: + GradientType( + gradient01: "B876E3", + gradient02: "AE66E0" + ) + ) + } + } + struct Pink: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "F792D7", + gradient02: "F684D3" + ), + stroke: + GradientType( + gradient01: "F687D4", + gradient02: "F477CD" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "F792D7", + gradient02: "F684D3" + ), + stroke: + GradientType( + gradient01: "F687D4", + gradient02: "F477CD" + ) + ) + } + } + struct Silver: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "8E97A1", + gradient02: "7F8A95" + ), + stroke: + GradientType( + gradient01: "838E98", + gradient02: "727D89" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "8E97A1", + gradient02: "7F8A95" + ), + stroke: + GradientType( + gradient01: "838E98", + gradient02: "727D89" + ) + ) + } + } + struct Khaki: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "9BA59C", + gradient02: "8E998E" + ), + stroke: + GradientType( + gradient01: "919D92", + gradient02: "818F83" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "9BA59C", + gradient02: "8E998E" + ), + stroke: + GradientType( + gradient01: "919D92", + gradient02: "818F83" + ) + ) + } + } + struct Brown: ColorProtocol { + var light: ColorType { + return ColorType(fill: + GradientType( + gradient01: "A39893", + gradient02: "968A85" + ), + stroke: + GradientType( + gradient01: "9A8E89", + gradient02: "8C7C78" + ) + ) + } + var dark: ColorType { + return ColorType(fill: + GradientType( + gradient01: "A39893", + gradient02: "968A85" + ), + stroke: + GradientType( + gradient01: "9A8E89", + gradient02: "8C7C78" + ) + ) + } + } + +} + +//정의된 LinearGradient +extension SCZColor { + static let promotionBanner = Color(hexString: "7AB5E9", opacity: 0.64) + static let promotionIndicator = LinearGradient( + colors: [CharcoalGray.color, + CharcoalGray.opacity48], + startPoint: .top, + endPoint: .bottom + ) + static let Basic = LinearGradient( + colors: [CharcoalGray.opacity64, + Color(hexString: "A6A6A6")], + startPoint: .top, + endPoint: .bottom + ) + static let Medium = LinearGradient( + colors: [CharcoalGray.opacity88, + CharcoalGray.opacity64], + startPoint: .top, + endPoint: .bottom + ) + static let Light = LinearGradient( + colors: [CharcoalGray.opacity88, + CharcoalGray.opacity48], + startPoint: .top, + endPoint: .bottom + ) + + + struct Seal { + static let gold = LinearGradient( + colors: [Color(hexString: "E4C139"), + Color(hexString: "8D7516")], + startPoint: .top, + endPoint: .bottom + ) + static let silver = LinearGradient( + colors: [Color(hexString: "999999"), + Color(hexString: "717171")], + startPoint: .top, + endPoint: .bottom + ) + static let bronze = LinearGradient( + colors: [Color(hexString: "E6BA92"), + Color(hexString: "A66F3B")], + startPoint: .top, + endPoint: .bottom + ) + static let iron = LinearGradient( + colors: [CharcoalGray.opacity16], + startPoint: .top, + endPoint: .bottom + ) + static let defaultColor = LinearGradient( + colors: [CharcoalGray.opacity08], + startPoint: .top, + endPoint: .bottom + ) + static let textGold = LinearGradient( + colors: [Color(hexString: "E4C139"), + Color(hexString: "8D7516")], + startPoint: .bottom, + endPoint: .top + ) + static let textSilver = LinearGradient( + colors: [Color(hexString: "999999"), + Color(hexString: "717171")], + startPoint: .bottom, + endPoint: .top + ) + static let textBronze = LinearGradient( + colors: [Color(hexString: "E6BA92"), + Color(hexString: "A66F3B")], + startPoint: .bottom, + endPoint: .top + ) + static let textIron = LinearGradient( + colors: [CharcoalGray.opacity88], + startPoint: .bottom, + endPoint: .top + ) + static let textDefault = LinearGradient( + colors: [CharcoalGray.opacity48], + startPoint: .bottom, + endPoint: .top + ) + } + struct CharcoalGray { + static let color: Color = Color(hexString: "404040") + static let opacity88 = Color(hexString: "404040", opacity: 0.88) + static let opacity64 = Color(hexString: "404040", opacity: 0.64) + static let opacity48 = Color(hexString: "404040", opacity: 0.48) + static let opacity24 = Color(hexString: "404040", opacity: 0.24) + static let opacity16 = Color(hexString: "404040", opacity: 0.16) + static let opacity08 = Color(hexString: "404040", opacity: 0.08) + static let opacity04 = Color(hexString: "404040", opacity: 0.04) + } +} diff --git a/HappyAnding/HappyAnding/Model/ExploreShortcutSectionType.swift b/HappyAnding/HappyAnding/Model/ExploreShortcutSectionType.swift new file mode 100644 index 00000000..382d0162 --- /dev/null +++ b/HappyAnding/HappyAnding/Model/ExploreShortcutSectionType.swift @@ -0,0 +1,78 @@ +// +// ExploreShortcutSectionType.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +enum ExploreShortcutSectionType: String { + case new = "sparkles" + case mostDownloaded = "trophy.fill" + case mostLoved = "heart.fill" + + func getTitleImage() -> some View { + switch self { + case .new: + return Image(systemName: self.rawValue) + .foregroundStyle( + Color(hexString: "E4C139") + ) + + case .mostDownloaded: + return Image(systemName: self.rawValue) + .foregroundStyle( + Color(hexString: "404040", opacity: 0.48) + ) + case .mostLoved: + return Image(systemName: self.rawValue) + .foregroundStyle( + Color(hexString: "3366FF", opacity: 0.88) + ) + } + } + func getTitleView() -> some View { + switch self { + case .new: + return HStack { + Image(systemName: self.rawValue) + .foregroundStyle( + Color(hexString: "E4C139") + ) + Text(TextLiteral.newShortcutsTitle) + .foregroundStyle(SCZColor.Basic) + } + + case .mostDownloaded: + return HStack { + Image(systemName: self.rawValue) + .foregroundStyle( + Color(hexString: "404040", opacity: 0.48) + ) + Text(TextLiteral.downloadRankViewTitle) + .foregroundStyle(SCZColor.Basic) + } + case .mostLoved: + return HStack { + Image(systemName: self.rawValue) + .foregroundStyle( + Color(hexString: "3366FF", opacity: 0.88) + ) + Text(TextLiteral.lovedShortcutViewTitle) + .foregroundStyle(SCZColor.Basic) + } + } + } + + func filterShortcuts(from viewModel: ShortcutsZipViewModel) -> [Shortcuts] { + switch self { + case .new: + return viewModel.allShortcuts + case .mostDownloaded: + return viewModel.sortedShortcutsByDownload + case .mostLoved: + return viewModel.sortedShortcutsByLike + } + } +} diff --git a/HappyAnding/HappyAnding/Model/SectionType.swift b/HappyAnding/HappyAnding/Model/SectionType.swift index 9348a69e..34e040b9 100644 --- a/HappyAnding/HappyAnding/Model/SectionType.swift +++ b/HappyAnding/HappyAnding/Model/SectionType.swift @@ -6,6 +6,7 @@ // import Foundation +import SwiftUI enum SectionType { case recent @@ -15,6 +16,22 @@ enum SectionType { case myLovingShortcut case myDownloadShortcut + var icon: String { + switch self { + case .recent: + return "sparkles" + case .download: + return "trophy.fill" + case .popular: + return "heart.fill" + case .myShortcut: + return "square.text.square" + case .myLovingShortcut: + return "heart.fill" + case .myDownloadShortcut: + return "arrow.down.square.fill" + } + } var title: String { switch self { case .recent: @@ -48,4 +65,45 @@ enum SectionType { return viewModel.shortcutsUserDownloaded } } + + func fetchTitleIcon() -> some View { + switch self { + case .recent: + return Image(systemName: self.icon) + .foregroundStyle( + Color(hexString: "E4C139"), + Color(hexString: "E4C139") + ) + case .download: + return Image(systemName: self.icon) + .foregroundStyle( + Color(hexString: "404040", opacity: 0.48), + Color(hexString: "404040", opacity: 0.48) + ) + case .popular: + return Image(systemName: self.icon) + .foregroundStyle( + Color(hexString: "3366FF", opacity: 0.88), + Color(hexString: "3366FF", opacity: 0.88) + ) + case .myShortcut: + return Image(systemName: "square.text.square.fill") + .foregroundStyle( + SCZColor.CharcoalGray.opacity64, + Color.white + ) + case .myDownloadShortcut: + return Image(systemName: "arrow.down.square.fill") + .foregroundStyle( + Color.white, + SCZColor.CharcoalGray.opacity24 + ) + case .myLovingShortcut: + return Image(systemName: "heart.fill") + .foregroundStyle( + Color(hexString: "3366FF", opacity: 0.88), + Color(hexString: "3366FF", opacity: 0.88) + ) + } + } } diff --git a/HappyAnding/HappyAnding/TextLiteral.swift b/HappyAnding/HappyAnding/TextLiteral.swift index 4c16ad38..487884f1 100644 --- a/HappyAnding/HappyAnding/TextLiteral.swift +++ b/HappyAnding/HappyAnding/TextLiteral.swift @@ -31,7 +31,7 @@ enum TextLiteral { static let appStoreUrl: String = "itms-apps://itunes.apple.com/app/6444001181" // MARK: - ExploreShortcutView - static let exploreShortcutViewTitle: String = "단축어 둘러보기" + static let exploreShortcutViewTitle: String = "둘러보기" //MARK: - announcementCell static let newFeatureTag: String = "새로운 기능" @@ -53,9 +53,10 @@ enum TextLiteral { // MARK: - RecentRegisteredView static let recentRegisteredViewTitle: String = "최신 단축어" + static let newShortcutsTitle: String = "새로 올라온" // MARK: - LovedShortcutView - static let lovedShortcutViewTitle: String = "사랑받는 단축어" + static let lovedShortcutViewTitle: String = "사랑받는" // MARK: - DownloadRankView static let downloadRankViewTitle: String = "다운로드 순위" @@ -66,7 +67,7 @@ enum TextLiteral { static let categoryViewFold: String = "접기" // MARK: - MyShortcutCardListView - static let myShortcutCardListViewTitle: String = "내가 작성한 단축어" + static let myShortcutCardListViewTitle: String = "작성한 단축어" // MARK: - ValidationCheckTextField static let validationCheckTextFieldInvalid: String = "단축어 링크가 아니에요" @@ -225,8 +226,8 @@ enum TextLiteral { // MARK: - MyPageView static let myPageViewTitle: String = "프로필" static let myPageViewMyCuration: String = "내가 작성한 추천 모음집" - static let myPageViewLikedShortcuts: String = "좋아요한 단축어" - static let myPageViewDownloadedShortcuts: String = "다운로드한 단축어" + static let myPageViewLikedShortcuts: String = "내가 좋아요한" + static let myPageViewDownloadedShortcuts: String = "다운로드한" // MARK: - MailView static let mailViewReceiver: String = "shortcutszip@gmail.com" diff --git a/HappyAnding/HappyAnding/ViewModel/ExploreShortcutViewModels/ExploreShortcutViewModel.swift b/HappyAnding/HappyAnding/ViewModel/ExploreShortcutViewModels/ExploreShortcutViewModel.swift index dca00d12..17e124a1 100644 --- a/HappyAnding/HappyAnding/ViewModel/ExploreShortcutViewModels/ExploreShortcutViewModel.swift +++ b/HappyAnding/HappyAnding/ViewModel/ExploreShortcutViewModels/ExploreShortcutViewModel.swift @@ -27,8 +27,12 @@ final class ExploreShortcutViewModel: ObservableObject { func fetchShortcuts(by category: Category) -> [Shortcuts] { self.shortcutsZipViewModel.shortcutsInCategory[category.index] } - + func fetchShortcuts(by sectionType: SectionType) -> [Shortcuts] { sectionType.filterShortcuts(from: self.shortcutsZipViewModel) } + + func fetchAdminCuration() -> [Curation] { + self.shortcutsZipViewModel.adminCurations + } } diff --git a/HappyAnding/HappyAnding/Views/Components/CardSection.swift b/HappyAnding/HappyAnding/Views/Components/CardSection.swift new file mode 100644 index 00000000..6ffa2df3 --- /dev/null +++ b/HappyAnding/HappyAnding/Views/Components/CardSection.swift @@ -0,0 +1,43 @@ +// +// CardSection.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +struct CardSection: View { + let type: SectionType + let shortcuts: [Shortcuts] + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + HStack { + type.fetchTitleIcon() + Text(type.title) + .foregroundStyle(SCZColor.Basic) + } + .font(.system(size: 20, weight: .semibold)) + .padding(.horizontal, 13) + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 6) { + Rectangle() + .foregroundStyle(Color.clear) + .frame(width: 13) + ForEach(Array(shortcuts.enumerated()).prefix(5), id: \.offset) { index, shortcut in + switch type { + case .recent: + UnorderedCell(shortcut: shortcut) + default: + OrderedCell(type: .download, index: index, shortcut: shortcut) + } + } + ExpandedCell(type: type, shortcuts: shortcuts) + .padding(.trailing, 13) + } + } + } + .padding(.vertical, 4) + } +} diff --git a/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift b/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift new file mode 100644 index 00000000..4d00396e --- /dev/null +++ b/HappyAnding/HappyAnding/Views/Components/ExploreCell.swift @@ -0,0 +1,117 @@ +// +// ExploreCell.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +struct OrderedCell: View { + @Environment(\.colorScheme) var colorScheme + + let type: SectionType + let index: Int + let shortcut: Shortcuts + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + if type == .download || type == .popular { + Seal(index: index, type: .exploreCell) + } + Text(shortcut.title) + .foregroundStyle(SCZColor.Basic) + .font(.system(size: 15, weight: .bold)) + .multilineTextAlignment(.leading) + .lineLimit(2) + .blendMode(.plusDarker) + Spacer() + HStack(spacing: 8) { + HStack(spacing: 3) { + Image(systemName: "arrow.down.to.line.circle.fill") + .frame(width: 14) + Text(formatNumber(shortcut.numberOfDownload)) + .font(.system(size: 11, weight: .medium)) + .lineLimit(1) + .frame(maxWidth: .infinity, alignment: .leading) + } + .foregroundStyle(SCZColor.CharcoalGray.opacity24) + .blendMode(.colorBurn) + Image(systemName: shortcut.sfSymbol) + .foregroundStyle(Color.white) + .font(.system(size: 20)) + } + + } + .padding(12) + .frame(width: 108, height: 144, alignment: .top) + .background( SCZColor.colors[shortcut.color]?.color(for: colorScheme).fillGradient() ?? SCZColor.defaultColor) + .cornerRadius(16) + .roundedBorder(cornerRadius: 16, color: Color.white, isNormalBlend: true, opacity: 0.12) + } + + private func formatNumber(_ number: Int) -> String { + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + return numberFormatter.string(from: NSNumber(value: number))! + } +} + +struct UnorderedCell: View{ + @Environment(\.colorScheme) var colorScheme + + let shortcut: Shortcuts + var body: some View { + VStack(alignment: .leading, spacing: 4) { + HStack { + RoundedRectangle(cornerRadius: 1) + .frame(width: 2, height: 30) + .foregroundStyle(SCZColor.colors[shortcut.color]?.color(for: colorScheme).fillGradient() ?? SCZColor.defaultColor) + Image(systemName: shortcut.sfSymbol) + .foregroundStyle(SCZColor.CharcoalGray.opacity88) + } + Text(shortcut.title) + .foregroundStyle(SCZColor.Basic) + .frame(maxWidth: .infinity, alignment: .leading) + } + .font(.system(size: 15, weight: .bold)) + .padding(12) + .frame(width: 108, height: 144, alignment: .top) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(Color.white) + .overlay( + RoundedRectangle(cornerRadius: 16) + .stroke(SCZColor.Basic.opacity(0.12), lineWidth: 2) + ) + ) + .cornerRadius(16) + .dropShadow() + } +} + +struct ExpandedCell: View { + let type: SectionType + let shortcuts: [Shortcuts] + + var body: some View { + NavigationLink { + ExpandedRankingView(type: type, shortcuts: shortcuts) + } label: { + VStack(alignment: .center, spacing: 4) { + Image(systemName: "rectangle.portrait.on.rectangle.portrait.angled.fill") + Text("더보기") + } + .foregroundStyle(Color.white.opacity(0.88)) + .font(.system(size: 15, weight: .semibold)) + .frame(width: 108, height: 144, alignment: .center) + .background(SCZColor.CharcoalGray.opacity08) + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .strokeBorder(Color.white.opacity(0.12), lineWidth: 2) + ) + .shadow(color: SCZColor.CharcoalGray.opacity04, radius: 4, x: 0, y: 2) + } + } +} diff --git a/HappyAnding/HappyAnding/Views/Components/PromotionSection.swift b/HappyAnding/HappyAnding/Views/Components/PromotionSection.swift new file mode 100644 index 00000000..87495187 --- /dev/null +++ b/HappyAnding/HappyAnding/Views/Components/PromotionSection.swift @@ -0,0 +1,115 @@ +// +// PromotionSection.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +//TODO: 폰트 적용 및 PromotionCard shortDescription 적용 필요 +struct PromoteSection: View { + + let items: [Curation] + + let pageCount: Int + let visibleEdgeSpace: CGFloat = 10 + let spacing: CGFloat = 6 + + @GestureState var dragOffset: CGFloat = 0 + @State var currentIndex: Int = 0 + + init(items: [Curation]) { + self.items = items + self.pageCount = items.count + } + + var body: some View { + VStack(spacing: 16) { + TabView(selection: $currentIndex) { + ForEach(0.. 3 ? 2 : 3 + + ForEach(Array(promotion.shortcuts.enumerated()).prefix(maxIcons), id: \.offset) { index, shortcut in + ShortcutIcon(sfSymbol: shortcut.sfSymbol, color: shortcut.color, size: 66) + } + if maxIcons == 2 { + MoreShortcutIcon(count: count - 2) + } + } + .padding(.vertical, 13) + Text(promotion.subtitle.replacingOccurrences(of: "\\n", with: "\n")) + .font(Font.system(size: 14)) + .multilineTextAlignment(.center) + } + .padding(.horizontal, 16) + .padding(.vertical, 24) + } + .frame(width: UIScreen.screenWidth-(16*2), height: 240) + } + + var backgroundImage: some View { + ZStack { + //TODO: 이미지 변경 필요 + Image("bannerBg") + .resizable() + .aspectRatio(contentMode: .fill) + .blur(radius: 12) + Rectangle() + .foregroundStyle(SCZColor.promotionBanner) + .blendMode(.colorDodge) + + } + .frame(height: 240) + .clipShape(RoundedRectangle(cornerRadius: 16)) + .padding(.horizontal, 16) + } +} + +struct MoreShortcutIcon: View { + let count: Int + var body: some View { + Text("+\(count)") + .foregroundStyle(SCZColor.CharcoalGray.opacity48) + .font(.system(size: 24, weight: .medium)) + .frame(width: 66, height: 66) + .background(Color.white.opacity(0.12)) + .roundedBorder(cornerRadius: 13, color: Color.white, isNormalBlend: true, opacity: 0.24) + } +} diff --git a/HappyAnding/HappyAnding/Views/Components/Seal.swift b/HappyAnding/HappyAnding/Views/Components/Seal.swift new file mode 100644 index 00000000..b4696c11 --- /dev/null +++ b/HappyAnding/HappyAnding/Views/Components/Seal.swift @@ -0,0 +1,117 @@ +// +// Seal.swift +// HappyAnding +// +// Created by JeonJimin on 4/3/24. +// + +import SwiftUI + +enum SealType { + case exploreCell + case ranking +} +struct Seal: View { + let index: Int + let type: SealType + + var body: some View { + ZStack { + Image(systemName: "seal.fill") + .resizable() + .scaledToFit() + .frame(width: fetchSealSize()) + .foregroundStyle(fetchSealColor()) + Image(systemName: "seal") + .resizable() + .scaledToFit() + .frame(width: fetchSealSize()) + .foregroundStyle(fetchSealStrokeColor()) + Text("\(index)") + //TODO: 폰트 적용 필요 + .font(.system(size: 15, weight: .medium)) + .foregroundStyle(fetchTextColor()) + .blendMode(fetchBlendMode()) + } + } + + private func fetchSealColor() -> LinearGradient { + switch type { + case .ranking: + switch index { + case 1: + return SCZColor.Seal.gold + case 2: + return SCZColor.Seal.silver + case 3: + return SCZColor.Seal.bronze + case 4,5: + return SCZColor.Seal.iron + default: + return SCZColor.Seal.defaultColor + + } + case .exploreCell: + return Color.white.opacity(0.64).toGradient() + } + } + + private func fetchSealStrokeColor() -> Color { + switch type { + case .ranking: + switch index { + case 1,2,3: + return Color.white.opacity(0.12) + case 4,5: + return SCZColor.CharcoalGray.opacity04 + default: + return Color.clear + } + case .exploreCell: + return Color.white.opacity(0.24) + } + + } + private func fetchTextColor() -> LinearGradient { + switch type { + case .ranking: + switch index { + case 1: + return SCZColor.Seal.textGold + case 2: + return SCZColor.Seal.textSilver + case 3: + return SCZColor.Seal.textBronze + case 4,5: + return SCZColor.Seal.textIron + default: + return SCZColor.Seal.textDefault + } + case .exploreCell: + return SCZColor.CharcoalGray.opacity64.toGradient() + } + } + + private func fetchBlendMode() -> BlendMode { + switch type { + case .ranking: + switch index{ + case 1, 2, 3: + return .multiply + case 4,5: + return .colorBurn + default: + return .normal + } + case .exploreCell: + return .colorBurn + } + } + + private func fetchSealSize() -> CGFloat { + switch type { + case .ranking, .exploreCell: + return 28 + } + } +} diff --git a/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift b/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift new file mode 100644 index 00000000..3fbee17d --- /dev/null +++ b/HappyAnding/HappyAnding/Views/Components/ShortcutIcon.swift @@ -0,0 +1,32 @@ +// +// ShortcutIcon.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +struct ShortcutIcon: View { + @Environment(\.colorScheme) var colorScheme + + let sfSymbol: String + let color: String + let size: CGFloat + + var body: some View { + ZStack { + RoundedRectangle(cornerRadius: 13) + .foregroundStyle(SCZColor.colors[color]?.color(for: colorScheme).fillGradient() ?? SCZColor.defaultColor) + .roundedBorder(cornerRadius: 13, color: .white, isNormalBlend: true, opacity: 0.24) + .frame(width: size, height: size) + Image(systemName: sfSymbol) + .font(.system(size: 28)) + .foregroundStyle(Color.white) + } + } +} + +#Preview { + ShortcutIcon(sfSymbol: "play.rectangle.fill", color: "Red", size: 56) +} diff --git a/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExpandedRankingView.swift b/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExpandedRankingView.swift new file mode 100644 index 00000000..118f29cc --- /dev/null +++ b/HappyAnding/HappyAnding/Views/ExploreShortcutViews/ExpandedRankingView.swift @@ -0,0 +1,77 @@ +// +// ExpandedRankingView.swift +// HappyAnding +// +// Created by JeonJimin on 4/2/24. +// + +import SwiftUI + +struct ExpandedRankingView: View { + let type: SectionType + let shortcuts: [Shortcuts] + + var body: some View { + ScrollView(.vertical) { + VStack(spacing: 5) { + ForEach(0.. some View { - - let shortcuts = viewModel.fetchShortcuts(by: sectionType) - - VStack(spacing: 0) { - HStack { - SubtitleTextView(text: sectionType.title) - Spacer() - - MoreCaptionTextView(text: TextLiteral.more) - .navigationLinkRouter(data: sectionType) - } - .padding(.horizontal, 16) - .padding(.bottom, 12) - - ForEach(Array(shortcuts.enumerated()), id:\.offset) { index, shortcut in - if index < 3 { - ShortcutCell(shortcut: shortcut, - rankNumber: (sectionType == .download) ? index : nil, - navigationParentView: .shortcuts) - .navigationLinkRouter(data: shortcut) - } } - .background(Color.shortcutsZipBackground) + .padding(.bottom, 40) } - } - - @ViewBuilder - private func categoryCardView(with category: Category) -> some View { - - VStack(spacing: 0) { - HStack { - - SubtitleTextView(text: category.translateName()) - - Spacer() - - MoreCaptionTextView(text: TextLiteral.more) - .navigationLinkRouter(data: category) - } - .padding(.horizontal, 16) - .padding(.bottom, 12) - - ScrollView(.horizontal, showsIndicators: false) { + .toolbar{ + ToolbarItem(placement: .topBarTrailing) { HStack { - ForEach(viewModel.fetchShortcuts(by: category).prefix(7), id: \.self) { shortcut in - - ShortcutCardCell(categoryShortcutIcon: shortcut.sfSymbol, - categoryShortcutName: shortcut.title, - categoryShortcutColor: shortcut.color) - .navigationLinkRouter(data: shortcut) + Button { + //TODO: 알림창 연결 + withAnimation { + isSearchActivated.toggle() + } + print("검색창") + } label: { + Image(systemName: "sparkle.magnifyingglass") + .symbolRenderingMode(.palette) + .foregroundStyle( + LinearGradient( + colors: [SCZColor.CharcoalGray.opacity88, SCZColor.CharcoalGray.opacity48], + startPoint: .top, + endPoint: .bottom + ) + .opacity(0.64) + ) + } + Button { + //TODO: 알림창 연결 + print("알림창 연결") + } label: { + Image(systemName: "bell.badge.fill") + .symbolRenderingMode(.palette) + .foregroundStyle( + Color(hexString: "3366FF"), + LinearGradient( + colors: [SCZColor.CharcoalGray.opacity88, SCZColor.CharcoalGray.opacity48], + startPoint: .top, + endPoint: .bottom + ) + .opacity(0.64) + ) } } - .padding(.horizontal, 16) + + + } + ToolbarItem(placement: .topBarLeading) { + Text(TextLiteral.exploreShortcutViewTitle) + .font(.system(size: 24, weight: .bold)) + .foregroundStyle( + LinearGradient(colors: [SCZColor.CharcoalGray.color, SCZColor.CharcoalGray.opacity48], startPoint: .top, endPoint: .bottom) + ) } } - } - - @ViewBuilder - private func categoryCellView(with categoryName: String) -> some View { - RoundedRectangle(cornerSize: CGSize(width: 12, height: 12)) - .strokeBorder(Color.gray1, lineWidth: 1) - .background(Color.shortcutsZipWhite) - .cornerRadius(12) - .frame(maxWidth: .infinity, minHeight:48, maxHeight: 48) - .overlay { - Text(categoryName) - .shortcutsZipBody2() - .foregroundStyle(Color.gray5) + .navigationBarBackground({Color.clear}) + .background( + ZStack{ + Color.white + SCZColor.CharcoalGray.opacity04 } + .ignoresSafeArea() + ) } } - diff --git a/HappyAnding/HappyAnding/Views/SearchView.swift b/HappyAnding/HappyAnding/Views/SearchView.swift index 793fe19d..58ed256a 100644 --- a/HappyAnding/HappyAnding/Views/SearchView.swift +++ b/HappyAnding/HappyAnding/Views/SearchView.swift @@ -23,48 +23,10 @@ struct SearchView: View { var body: some View { VStack { - - searchTextfield - - if !isSearched { - recommendKeyword - Spacer() - } else { - if shortcutResults.count == 0 { - proposeView - } else { - ScrollView { - VStack(spacing: 0) { - ForEach(shortcutResults.sorted(by: { $0.title < $1.title }), id: \.self) { shortcut in - - ShortcutCell(shortcut: shortcut, - navigationParentView: NavigationParentView.shortcuts) - .navigationLinkRouter(data: shortcut) - .listRowInsets(EdgeInsets()) - .listRowSeparator(.hidden) - } - } - } - .scrollDismissesKeyboard(.immediately) - } - } - } - .onAppear() { - self.keywords = shortcutsZipViewModel.keywords - } - .onSubmit(of: .search, runSearch) - .onChange(of: searchText) { _ in - didChangedSearchText() - if !searchText.isEmpty { - isSearched = true - } else if searchText.isEmpty && !isSearching { - shortcutResults.removeAll() - isSearched = false - } + SearchBar() } - .navigationBarTitleDisplayMode(.inline) - .background(Color.shortcutsZipBackground) - .navigationBarBackground ({ Color.shortcutsZipBackground }) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color.black.opacity(0.13).ignoresSafeArea()) } private func runSearch() { @@ -160,8 +122,30 @@ struct SearchView: View { } } -struct SearchView_Previews: PreviewProvider { - static var previews: some View { - SearchView() +struct SearchBar: View { + @State var text: String = "" + + var body: some View { + HStack { + Image(systemName: "magnifyingglass") + TextField("단축어 제작의 기본", text: $text) + .frame(maxHeight: .infinity) + } + + .padding(.horizontal, 16) + .frame(height: 40) + .background( + Capsule() + .fill(SCZColor.CharcoalGray.opacity04) + .overlay( + Capsule() + .stroke(Color.white.opacity(0.12), lineWidth: 2) + ) + ) + .padding(.horizontal, 16) } } + +#Preview { + SearchView() +} diff --git a/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift b/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift index 6977f1ae..9f034133 100644 --- a/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift +++ b/HappyAnding/HappyAnding/Views/TabView/ShortcutTabView.swift @@ -34,6 +34,8 @@ struct ShortcutTabView: View { @State private var secondTabID = UUID() @State private var thirdTabID = UUID() + @State private var isSearchActivated = false + init() { let transparentAppearence = UITabBarAppearance() transparentAppearence.configureWithTransparentBackground() @@ -60,49 +62,62 @@ struct ShortcutTabView: View { var body: some View { ScrollViewReader { proxy in - TabView(selection: handler) { - NavigationRouter(content: firstTab, path: $shortcutNavigation.navigationPath) - .onChange(of: twiceTappedTab) { twiceTappedTab in - guard (twiceTappedTab != 0) else { return } - if twiceTappedTab == 1 { - didTappedTabViewItem(proxy, scrollID: 000, navigationPath: &shortcutNavigation.navigationPath, viewID: &firstTabID) - didTappedTabViewItem(proxy, scrollID: 111, navigationPath: &shortcutNavigation.navigationPath, viewID: &firstTabID) - } else if twiceTappedTab == 2 { - didTappedTabViewItem(proxy, scrollID: 222, navigationPath: &shortcutNavigation.navigationPath, viewID: &secondTabID) - } else { - didTappedTabViewItem(proxy, scrollID: 333, navigationPath: &shortcutNavigation.navigationPath, viewID: &thirdTabID) + ZStack { + TabView(selection: handler) { + NavigationRouter(content: firstTab, path: $shortcutNavigation.navigationPath) + .onChange(of: twiceTappedTab) { twiceTappedTab in + guard (twiceTappedTab != 0) else { return } + if twiceTappedTab == 1 { + didTappedTabViewItem(proxy, scrollID: 000, navigationPath: &shortcutNavigation.navigationPath, viewID: &firstTabID) + didTappedTabViewItem(proxy, scrollID: 111, navigationPath: &shortcutNavigation.navigationPath, viewID: &firstTabID) + } else if twiceTappedTab == 2 { + didTappedTabViewItem(proxy, scrollID: 222, navigationPath: &shortcutNavigation.navigationPath, viewID: &secondTabID) + } else { + didTappedTabViewItem(proxy, scrollID: 333, navigationPath: &shortcutNavigation.navigationPath, viewID: &thirdTabID) + } + self.twiceTappedTab = 0 } - self.twiceTappedTab = 0 - } - .environmentObject(shortcutNavigation) - .tabItem { - Label("단축어", systemImage: "square.stack.3d.up.fill") - } - .tag(1) - - NavigationRouter(content: secondTab, path: $curationNavigation.navigationPath) - .environmentObject(curationNavigation) - .tabItem { - Label("추천모음집", systemImage: "folder.fill") - } - .tag(2) - - NavigationRouter(content: thirdTab, path: $profileNavigation.navigationPath) - .environmentObject(profileNavigation) - .tabItem { - Label("프로필", systemImage: "person.crop.circle.fill") + .environmentObject(shortcutNavigation) + .tabItem { + Label("단축어", systemImage: "square.stack.3d.up.fill") + } + .tag(1) + + NavigationRouter(content: secondTab, path: $curationNavigation.navigationPath) + .environmentObject(curationNavigation) + .tabItem { + Label("추천모음집", systemImage: "folder.fill") + } + .tag(2) + + NavigationRouter(content: thirdTab, path: $profileNavigation.navigationPath) + .environmentObject(profileNavigation) + .tabItem { + Label("프로필", systemImage: "person.crop.circle.fill") + } + .tag(3) + } + .onChange(of: phase) { newPhase in + switch newPhase { + case .background: isShortcutDeeplink = false; isCurationDeeplink = false + default: break } - .tag(3) - } - .onChange(of: phase) { newPhase in - switch newPhase { - case .background: isShortcutDeeplink = false; isCurationDeeplink = false - default: break } - } - .onOpenURL { url in - fetchShortcutIdFromUrl(urlString: url.absoluteString) - fetchCurationIdFromUrl(urlString: url.absoluteString) + .onOpenURL { url in + fetchShortcutIdFromUrl(urlString: url.absoluteString) + fetchCurationIdFromUrl(urlString: url.absoluteString) + } + + if isSearchActivated { + Color.black.opacity(0.33) + .ignoresSafeArea() + .onTapGesture { + withAnimation { + isSearchActivated.toggle() + } + + } + } } } } @@ -110,7 +125,7 @@ struct ShortcutTabView: View { @ViewBuilder private func firstTab() -> some View { - ExploreShortcutView(viewModel: ExploreShortcutViewModel(), randomCategories: Array(randomCategories)) + ExploreShortcutView(viewModel: ExploreShortcutViewModel(), isSearchActivated: $isSearchActivated) .modifierNavigation() .navigationBarBackground ({ Color.shortcutsZipBackground }) .id(firstTabID)