diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 12818f10..ede83f1d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,3 @@ ---- -name: "Pull Request Template" -about: 기본 PR 템플릿입니다. -title: '[종류] #브랜치번호 - PR에 대한 설명' -labels: '' -assignees: '' - ---- - ## 🌁 Background @@ -14,6 +5,10 @@ assignees: '' ## 📱 Screenshot +| iPhone SE2 | iPhone 13 mini | iPhone 13 Pro | +|----|----|----| +| | | | + ## 👩‍💻 Contents diff --git a/GEON-PPANG-iOS.xcodeproj/project.pbxproj b/GEON-PPANG-iOS.xcodeproj/project.pbxproj index 0f860181..e20bf9c0 100644 --- a/GEON-PPANG-iOS.xcodeproj/project.pbxproj +++ b/GEON-PPANG-iOS.xcodeproj/project.pbxproj @@ -38,6 +38,15 @@ 0961C3642A501EBF0031A822 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0961C3632A501EBF0031A822 /* Strings.swift */; }; 097682DA2A5C829D0008F4FB /* GradientImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682D92A5C829D0008F4FB /* GradientImageView.swift */; }; 097682DC2A5C83910008F4FB /* CGFloat+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682DB2A5C83910008F4FB /* CGFloat+.swift */; }; + 097682DF2A5C91280008F4FB /* BakeryListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682DE2A5C91280008F4FB /* BakeryListResponseDTO.swift */; }; + 097682E32A5C95750008F4FB /* BakeryFilterCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682E22A5C95750008F4FB /* BakeryFilterCollectionViewCell.swift */; }; + 097682E52A5C99340008F4FB /* BakeryFilterItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682E42A5C99340008F4FB /* BakeryFilterItems.swift */; }; + 097682E72A5CF0080008F4FB /* BakeryFilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682E62A5CF0080008F4FB /* BakeryFilterView.swift */; }; + 097682E92A5D0B6B0008F4FB /* BakeryListTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682E82A5D0B6B0008F4FB /* BakeryListTopView.swift */; }; + 097682EB2A5D12BD0008F4FB /* BakeryListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 097682EA2A5D12BD0008F4FB /* BakeryListCollectionViewCell.swift */; }; + 098716B42A6061F500538D05 /* SearchResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098716B32A6061F500538D05 /* SearchResponseDTO.swift */; }; + 098716B62A60F52200538D05 /* BakeryTypeProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098716B52A60F52200538D05 /* BakeryTypeProtocol.swift */; }; + 098716B82A6138BD00538D05 /* MyReviewsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 098716B72A6138BD00538D05 /* MyReviewsHeaderView.swift */; }; 0987288D2A5BA1F000A29402 /* BookmarkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0987288C2A5BA1F000A29402 /* BookmarkButton.swift */; }; 098F32EA2A4200FE0092D09A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 098F32E92A4200FE0092D09A /* Assets.xcassets */; }; 098F32ED2A4200FE0092D09A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 098F32EB2A4200FE0092D09A /* LaunchScreen.storyboard */; }; @@ -49,7 +58,6 @@ 09B13F562A59410C00C0C723 /* CALayer+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B13F552A59410C00C0C723 /* CALayer+.swift */; }; 09B13F582A59433500C0C723 /* TabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B13F572A59433500C0C723 /* TabBarItem.swift */; }; 09B13F5A2A5946B700C0C723 /* TabBar+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B13F592A5946B700C0C723 /* TabBar+.swift */; }; - 09B71BFC2A59CF3E00076AC2 /* Protocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B71BFB2A59CF3E00076AC2 /* Protocol.swift */; }; 09B71C002A59D50900076AC2 /* HomeTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B71BFF2A59D50900076AC2 /* HomeTopView.swift */; }; 09B71C022A59D99200076AC2 /* SearchTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09B71C012A59D99200076AC2 /* SearchTextField.swift */; }; 09C6264E2A5B29F8002C8110 /* SearchEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09C6264D2A5B29F8002C8110 /* SearchEnum.swift */; }; @@ -60,26 +68,47 @@ 09CA3EFC2A569E4A0063897A /* URLConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CA3EFB2A569E4A0063897A /* URLConstant.swift */; }; 09CA3F062A569EB30063897A /* API.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CA3F052A569EB30063897A /* API.swift */; }; 09CA3F082A569EBA0063897A /* Service.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09CA3F072A569EBA0063897A /* Service.swift */; }; + 09FD47C82A5DC991002020BD /* SearchNavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FD47C72A5DC991002020BD /* SearchNavigationView.swift */; }; + 09FD47CA2A5DD38D002020BD /* SearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FD47C92A5DD38D002020BD /* SearchResultView.swift */; }; + 09FD47D22A5DF892002020BD /* EmptyCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FD47D12A5DF892002020BD /* EmptyCollectionViewCell.swift */; }; + 09FD47D92A5E5148002020BD /* MySavedBakeryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FD47D82A5E5148002020BD /* MySavedBakeryViewController.swift */; }; + 09FD47DD2A5E57F2002020BD /* MyReviewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09FD47DC2A5E57F2002020BD /* MyReviewsViewController.swift */; }; 3E16E4F52A57CF3800B813D0 /* WriteReviewDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E16E4F42A57CF3800B813D0 /* WriteReviewDTO.swift */; }; 3E16E4FA2A57D1F400B813D0 /* RecommendationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E16E4F92A57D1F400B813D0 /* RecommendationModel.swift */; }; 3E16E4FC2A57D24900B813D0 /* LikeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E16E4FB2A57D24900B813D0 /* LikeModel.swift */; }; 3E16E4FE2A57F06D00B813D0 /* OptionsCollectionViewFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E16E4FD2A57F06D00B813D0 /* OptionsCollectionViewFlowLayout.swift */; }; - 3E16E5002A57FFD900B813D0 /* OptionsCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E16E4FF2A57FFD900B813D0 /* OptionsCollectionViewHeader.swift */; }; 3E16E5022A58176800B813D0 /* ReviewDetailTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E16E5012A58176800B813D0 /* ReviewDetailTextView.swift */; }; + 3E2EEF752A5C93010093BCA9 /* BottomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E2EEF742A5C93010093BCA9 /* BottomView.swift */; }; + 3E452B672A60E68000EA456D /* MyPageDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E452B662A60E68000EA456D /* MyPageDTO.swift */; }; + 3E452B692A60FF7900EA456D /* MyPageCollectionViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E452B682A60FF7900EA456D /* MyPageCollectionViewFooter.swift */; }; + 3E452B6B2A614AAE00EA456D /* ImageWithSubtitleButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E452B6A2A614AAD00EA456D /* ImageWithSubtitleButton.swift */; }; 3E79B19D2A54523D00D36A26 /* BackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B19C2A54523D00D36A26 /* BackButton.swift */; }; + 3E79B1A02A5546E700D36A26 /* DescriptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B19F2A5546E700D36A26 /* DescriptionCollectionViewCell.swift */; }; 3E79B1A62A566F6400D36A26 /* WriteReviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B1A52A566F6400D36A26 /* WriteReviewViewController.swift */; }; 3E79B1AE2A56F14A00D36A26 /* BakeryOverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B1AD2A56F14A00D36A26 /* BakeryOverviewView.swift */; }; - 3E79B1B02A571B4B00D36A26 /* LineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B1AF2A571B4B00D36A26 /* LineView.swift */; }; 3E79B1B22A571E1F00D36A26 /* OptionsCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B1B12A571E1F00D36A26 /* OptionsCollectionView.swift */; }; 3E79B1B52A571E7100D36A26 /* OptionsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B1B42A571E7100D36A26 /* OptionsCollectionViewCell.swift */; }; - 3E79B1A02A5546E700D36A26 /* DescriptionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E79B19F2A5546E700D36A26 /* DescriptionCollectionViewCell.swift */; }; 3EA2E1E32A53E88B003516A3 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA2E1E22A53E88B003516A3 /* BaseViewController.swift */; }; - 3EA2E1E92A541AEB003516A3 /* CustomNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA2E1E82A541AEB003516A3 /* CustomNavigationBar.swift */; }; 3EA2E1EB2A542151003516A3 /* SizeLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA2E1EA2A542151003516A3 /* SizeLiteral.swift */; }; + 3ED40DF92A5FCF3800C7D5DA /* FilterEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED40DF82A5FCF3800C7D5DA /* FilterEnum.swift */; }; + 3ED40DFB2A5FD26200C7D5DA /* FilterDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED40DFA2A5FD26200C7D5DA /* FilterDTO.swift */; }; + 3ED40E002A608B4700C7D5DA /* MyPageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED40DFF2A608B4600C7D5DA /* MyPageCollectionViewCell.swift */; }; + 3ED40E022A608B8600C7D5DA /* MyPageSectionEnum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED40E012A608B8600C7D5DA /* MyPageSectionEnum.swift */; }; + 3ED40E042A608EF000C7D5DA /* MyPageCollectionViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED40E032A608EF000C7D5DA /* MyPageCollectionViewHeader.swift */; }; + 3ED40E062A60916000C7D5DA /* MyPagePurposeChipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED40E052A60916000C7D5DA /* MyPagePurposeChipView.swift */; }; + 3EDC05BE2A5DED4400EAD950 /* FilterPurposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDC05BD2A5DED4400EAD950 /* FilterPurposeViewController.swift */; }; + 3EDC05C02A5DED9200EAD950 /* FilterBreadTypeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDC05BF2A5DED9200EAD950 /* FilterBreadTypeViewController.swift */; }; + 3EDC05C22A5DED9D00EAD950 /* FilterIngredientViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDC05C12A5DED9D00EAD950 /* FilterIngredientViewController.swift */; }; + 3EDC05C42A5DEE3600EAD950 /* FilterCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDC05C32A5DEE3600EAD950 /* FilterCollectionViewCell.swift */; }; + 3EDC05D02A5E4FF900EAD950 /* String+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDC05CF2A5E4FF900EAD950 /* String+.swift */; }; + 3EFA97862A5EBCA9002E70FA /* LineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFA97852A5EBCA9002E70FA /* LineView.swift */; }; + 3EFA97882A5EBCED002E70FA /* CustomNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFA97872A5EBCED002E70FA /* CustomNavigationBar.swift */; }; DF959A612A539FBE00E75774 /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = DF959A5F2A539FBE00E75774 /* Pretendard-Bold.otf */; }; DF959A622A539FBE00E75774 /* Pretendard-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = DF959A602A539FBE00E75774 /* Pretendard-Medium.otf */; }; DF959A6C2A568C4600E75774 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DF959A6B2A568C4600E75774 /* Colors.xcassets */; }; DF959A6E2A568C9400E75774 /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF959A6D2A568C9400E75774 /* UIColor+.swift */; }; + DFB587B92A5D584800704B6C /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB587B82A5D584800704B6C /* OnboardingViewController.swift */; }; + DFB587BB2A5D588500704B6C /* DrawDashLineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DFB587BA2A5D588500704B6C /* DrawDashLineView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -107,6 +136,15 @@ 0961C3632A501EBF0031A822 /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; 097682D92A5C829D0008F4FB /* GradientImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientImageView.swift; sourceTree = ""; }; 097682DB2A5C83910008F4FB /* CGFloat+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+.swift"; sourceTree = ""; }; + 097682DE2A5C91280008F4FB /* BakeryListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryListResponseDTO.swift; sourceTree = ""; }; + 097682E22A5C95750008F4FB /* BakeryFilterCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryFilterCollectionViewCell.swift; sourceTree = ""; }; + 097682E42A5C99340008F4FB /* BakeryFilterItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryFilterItems.swift; sourceTree = ""; }; + 097682E62A5CF0080008F4FB /* BakeryFilterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryFilterView.swift; sourceTree = ""; }; + 097682E82A5D0B6B0008F4FB /* BakeryListTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryListTopView.swift; sourceTree = ""; }; + 097682EA2A5D12BD0008F4FB /* BakeryListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryListCollectionViewCell.swift; sourceTree = ""; }; + 098716B32A6061F500538D05 /* SearchResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResponseDTO.swift; sourceTree = ""; }; + 098716B52A60F52200538D05 /* BakeryTypeProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryTypeProtocol.swift; sourceTree = ""; }; + 098716B72A6138BD00538D05 /* MyReviewsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyReviewsHeaderView.swift; sourceTree = ""; }; 0987288C2A5BA1F000A29402 /* BookmarkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkButton.swift; sourceTree = ""; }; 098F32DD2A4200FD0092D09A /* GEON-PPANG-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "GEON-PPANG-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 098F32E02A4200FD0092D09A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -122,7 +160,6 @@ 09B13F552A59410C00C0C723 /* CALayer+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CALayer+.swift"; sourceTree = ""; }; 09B13F572A59433500C0C723 /* TabBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarItem.swift; sourceTree = ""; }; 09B13F592A5946B700C0C723 /* TabBar+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TabBar+.swift"; sourceTree = ""; }; - 09B71BFB2A59CF3E00076AC2 /* Protocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocol.swift; sourceTree = ""; }; 09B71BFF2A59D50900076AC2 /* HomeTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTopView.swift; sourceTree = ""; }; 09B71C012A59D99200076AC2 /* SearchTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTextField.swift; sourceTree = ""; }; 09C6264D2A5B29F8002C8110 /* SearchEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchEnum.swift; sourceTree = ""; }; @@ -133,26 +170,47 @@ 09CA3EFB2A569E4A0063897A /* URLConstant.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLConstant.swift; sourceTree = ""; }; 09CA3F052A569EB30063897A /* API.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = API.swift; sourceTree = ""; }; 09CA3F072A569EBA0063897A /* Service.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Service.swift; sourceTree = ""; }; + 09FD47C72A5DC991002020BD /* SearchNavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchNavigationView.swift; sourceTree = ""; }; + 09FD47C92A5DD38D002020BD /* SearchResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultView.swift; sourceTree = ""; }; + 09FD47D12A5DF892002020BD /* EmptyCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyCollectionViewCell.swift; sourceTree = ""; }; + 09FD47D82A5E5148002020BD /* MySavedBakeryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MySavedBakeryViewController.swift; sourceTree = ""; }; + 09FD47DC2A5E57F2002020BD /* MyReviewsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyReviewsViewController.swift; sourceTree = ""; }; 3E16E4F42A57CF3800B813D0 /* WriteReviewDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteReviewDTO.swift; sourceTree = ""; }; 3E16E4F92A57D1F400B813D0 /* RecommendationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecommendationModel.swift; sourceTree = ""; }; 3E16E4FB2A57D24900B813D0 /* LikeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LikeModel.swift; sourceTree = ""; }; 3E16E4FD2A57F06D00B813D0 /* OptionsCollectionViewFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsCollectionViewFlowLayout.swift; sourceTree = ""; }; - 3E16E4FF2A57FFD900B813D0 /* OptionsCollectionViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsCollectionViewHeader.swift; sourceTree = ""; }; 3E16E5012A58176800B813D0 /* ReviewDetailTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewDetailTextView.swift; sourceTree = ""; }; + 3E2EEF742A5C93010093BCA9 /* BottomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BottomView.swift; sourceTree = ""; }; + 3E452B662A60E68000EA456D /* MyPageDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageDTO.swift; sourceTree = ""; }; + 3E452B682A60FF7900EA456D /* MyPageCollectionViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageCollectionViewFooter.swift; sourceTree = ""; }; + 3E452B6A2A614AAD00EA456D /* ImageWithSubtitleButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageWithSubtitleButton.swift; sourceTree = ""; }; 3E79B19C2A54523D00D36A26 /* BackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackButton.swift; sourceTree = ""; }; 3E79B19F2A5546E700D36A26 /* DescriptionCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DescriptionCollectionViewCell.swift; sourceTree = ""; }; 3E79B1A52A566F6400D36A26 /* WriteReviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteReviewViewController.swift; sourceTree = ""; }; 3E79B1AD2A56F14A00D36A26 /* BakeryOverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BakeryOverviewView.swift; sourceTree = ""; }; - 3E79B1AF2A571B4B00D36A26 /* LineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineView.swift; sourceTree = ""; }; 3E79B1B12A571E1F00D36A26 /* OptionsCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsCollectionView.swift; sourceTree = ""; }; 3E79B1B42A571E7100D36A26 /* OptionsCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionsCollectionViewCell.swift; sourceTree = ""; }; 3EA2E1E22A53E88B003516A3 /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; - 3EA2E1E82A541AEB003516A3 /* CustomNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationBar.swift; sourceTree = ""; }; 3EA2E1EA2A542151003516A3 /* SizeLiteral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeLiteral.swift; sourceTree = ""; }; + 3ED40DF82A5FCF3800C7D5DA /* FilterEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterEnum.swift; sourceTree = ""; }; + 3ED40DFA2A5FD26200C7D5DA /* FilterDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterDTO.swift; sourceTree = ""; }; + 3ED40DFF2A608B4600C7D5DA /* MyPageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageCollectionViewCell.swift; sourceTree = ""; }; + 3ED40E012A608B8600C7D5DA /* MyPageSectionEnum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageSectionEnum.swift; sourceTree = ""; }; + 3ED40E032A608EF000C7D5DA /* MyPageCollectionViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageCollectionViewHeader.swift; sourceTree = ""; }; + 3ED40E052A60916000C7D5DA /* MyPagePurposeChipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPagePurposeChipView.swift; sourceTree = ""; }; + 3EDC05BD2A5DED4400EAD950 /* FilterPurposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterPurposeViewController.swift; sourceTree = ""; }; + 3EDC05BF2A5DED9200EAD950 /* FilterBreadTypeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterBreadTypeViewController.swift; sourceTree = ""; }; + 3EDC05C12A5DED9D00EAD950 /* FilterIngredientViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterIngredientViewController.swift; sourceTree = ""; }; + 3EDC05C32A5DEE3600EAD950 /* FilterCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterCollectionViewCell.swift; sourceTree = ""; }; + 3EDC05CF2A5E4FF900EAD950 /* String+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+.swift"; sourceTree = ""; }; + 3EFA97852A5EBCA9002E70FA /* LineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineView.swift; sourceTree = ""; }; + 3EFA97872A5EBCED002E70FA /* CustomNavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomNavigationBar.swift; sourceTree = ""; }; DF959A5F2A539FBE00E75774 /* Pretendard-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.otf"; sourceTree = ""; }; DF959A602A539FBE00E75774 /* Pretendard-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Medium.otf"; sourceTree = ""; }; DF959A6B2A568C4600E75774 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; DF959A6D2A568C9400E75774 /* UIColor+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+.swift"; sourceTree = ""; }; + DFB587B82A5D584800704B6C /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; + DFB587BA2A5D588500704B6C /* DrawDashLineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DrawDashLineView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -197,6 +255,7 @@ 0905562C2A51DBE700752067 /* UILabel+.swift */, 09B13F552A59410C00C0C723 /* CALayer+.swift */, 09B13F592A5946B700C0C723 /* TabBar+.swift */, + 3EDC05CF2A5E4FF900EAD950 /* String+.swift */, ); path = UI; sourceTree = ""; @@ -246,6 +305,8 @@ 093214032A5AE3C000875EF6 /* Search */ = { isa = PBXGroup; children = ( + 098716B22A603A8500538D05 /* Model */, + 09FD47C62A5DC97E002020BD /* Views */, 093214052A5AE3D600875EF6 /* ViewControllers */, ); path = Search; @@ -255,6 +316,7 @@ isa = PBXGroup; children = ( 093214072A5AE40900875EF6 /* SearchViewController.swift */, + 09FD47D12A5DF892002020BD /* EmptyCollectionViewCell.swift */, ); path = ViewControllers; sourceTree = ""; @@ -337,7 +399,11 @@ 0961C36A2A501EF60031A822 /* Presentation */ = { isa = PBXGroup; children = ( + 3EDC05BA2A5DEB7500EAD950 /* Filter */, + DFB587B72A5D580B00704B6C /* Onboarding */, 3E79B1A42A566F0700D36A26 /* WriteReview */, + 09FD47D62A5E511F002020BD /* MySavedBakery */, + 09FD47DA2A5E57CC002020BD /* MyReviews */, 093214032A5AE3C000875EF6 /* Search */, 09B13F492A593C5900C0C723 /* Home */, 09B13F4D2A593C7600C0C723 /* BakeryList */, @@ -394,6 +460,40 @@ path = ImageView; sourceTree = ""; }; + 097682DD2A5C91050008F4FB /* Model */ = { + isa = PBXGroup; + children = ( + 097682E42A5C99340008F4FB /* BakeryFilterItems.swift */, + 097682DE2A5C91280008F4FB /* BakeryListResponseDTO.swift */, + ); + path = Model; + sourceTree = ""; + }; + 0979852E2A5D9DBF004263E5 /* Views */ = { + isa = PBXGroup; + children = ( + 097682E82A5D0B6B0008F4FB /* BakeryListTopView.swift */, + 097682E62A5CF0080008F4FB /* BakeryFilterView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 098716B22A603A8500538D05 /* Model */ = { + isa = PBXGroup; + children = ( + 098716B32A6061F500538D05 /* SearchResponseDTO.swift */, + ); + path = Model; + sourceTree = ""; + }; + 098716B92A613B6A00538D05 /* Views */ = { + isa = PBXGroup; + children = ( + 098716B72A6138BD00538D05 /* MyReviewsHeaderView.swift */, + ); + path = Views; + sourceTree = ""; + }; 098F32D42A4200FD0092D09A = { isa = PBXGroup; children = ( @@ -454,6 +554,7 @@ 09B13F4D2A593C7600C0C723 /* BakeryList */ = { isa = PBXGroup; children = ( + 0979852E2A5D9DBF004263E5 /* Views */, 09B13F4E2A593CAE00C0C723 /* ViewControllers */, ); path = BakeryList; @@ -463,6 +564,9 @@ isa = PBXGroup; children = ( 09B13F4F2A593CB800C0C723 /* BakeryListViewController.swift */, + 097682E22A5C95750008F4FB /* BakeryFilterCollectionViewCell.swift */, + 097682EA2A5D12BD0008F4FB /* BakeryListCollectionViewCell.swift */, + 097682DD2A5C91050008F4FB /* Model */, ); path = ViewControllers; sourceTree = ""; @@ -470,6 +574,8 @@ 09B13F512A593CC200C0C723 /* MyPage */ = { isa = PBXGroup; children = ( + 3ED40DFD2A608B2F00C7D5DA /* Model */, + 3ED40DFC2A608B2B00C7D5DA /* View */, 09B13F522A593CC800C0C723 /* ViewControllers */, ); path = MyPage; @@ -495,11 +601,53 @@ 09CA3EF62A56738A0063897A /* Protocol */ = { isa = PBXGroup; children = ( - 09B71BFB2A59CF3E00076AC2 /* Protocol.swift */, + 098716B52A60F52200538D05 /* BakeryTypeProtocol.swift */, ); path = Protocol; sourceTree = ""; }; + 09FD47C62A5DC97E002020BD /* Views */ = { + isa = PBXGroup; + children = ( + 09FD47C72A5DC991002020BD /* SearchNavigationView.swift */, + 09FD47C92A5DD38D002020BD /* SearchResultView.swift */, + ); + path = Views; + sourceTree = ""; + }; + 09FD47D62A5E511F002020BD /* MySavedBakery */ = { + isa = PBXGroup; + children = ( + 09FD47D72A5E5132002020BD /* ViewControllers */, + ); + path = MySavedBakery; + sourceTree = ""; + }; + 09FD47D72A5E5132002020BD /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 09FD47D82A5E5148002020BD /* MySavedBakeryViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; + 09FD47DA2A5E57CC002020BD /* MyReviews */ = { + isa = PBXGroup; + children = ( + 098716B92A613B6A00538D05 /* Views */, + 09FD47DB2A5E57E3002020BD /* ViewControllers */, + ); + path = MyReviews; + sourceTree = ""; + }; + 09FD47DB2A5E57E3002020BD /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 09FD47DC2A5E57F2002020BD /* MyReviewsViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; 3E16E4F22A57CE2B00B813D0 /* Model */ = { isa = PBXGroup; children = ( @@ -511,7 +659,6 @@ 3E16E4F32A57CEF400B813D0 /* Bakery */ = { isa = PBXGroup; children = ( - 3E16E4F42A57CF3800B813D0 /* WriteReviewDTO.swift */, ); path = Bakery; sourceTree = ""; @@ -539,15 +686,33 @@ children = ( 3E16E4FB2A57D24900B813D0 /* LikeModel.swift */, 3E16E4F92A57D1F400B813D0 /* RecommendationModel.swift */, + 3E16E4F42A57CF3800B813D0 /* WriteReviewDTO.swift */, ); path = Model; sourceTree = ""; }; + 3E2EEF722A5C928F0093BCA9 /* View */ = { + isa = PBXGroup; + children = ( + 3EFA97852A5EBCA9002E70FA /* LineView.swift */, + 3E2EEF742A5C93010093BCA9 /* BottomView.swift */, + ); + path = View; + sourceTree = ""; + }; + 3E2EEF732A5C92980093BCA9 /* NavigationBar */ = { + isa = PBXGroup; + children = ( + 3EFA97872A5EBCED002E70FA /* CustomNavigationBar.swift */, + ); + path = NavigationBar; + sourceTree = ""; + }; 3E79B19B2A54523400D36A26 /* Button */ = { isa = PBXGroup; children = ( - 0987288C2A5BA1F000A29402 /* BookmarkButton.swift */, 3E79B19C2A54523D00D36A26 /* BackButton.swift */, + 0987288C2A5BA1F000A29402 /* BookmarkButton.swift */, 09CA3EF72A5676AA0063897A /* CommonButton.swift */, ); path = Button; @@ -577,7 +742,6 @@ 3E79B1B12A571E1F00D36A26 /* OptionsCollectionView.swift */, 3E79B1B42A571E7100D36A26 /* OptionsCollectionViewCell.swift */, 3E16E4FD2A57F06D00B813D0 /* OptionsCollectionViewFlowLayout.swift */, - 3E16E4FF2A57FFD900B813D0 /* OptionsCollectionViewHeader.swift */, ); path = OptionsCollection; sourceTree = ""; @@ -585,6 +749,7 @@ 3EA2E1E02A53E864003516A3 /* Common */ = { isa = PBXGroup; children = ( + 3E2EEF722A5C928F0093BCA9 /* View */, 093213FD2A5A74D100875EF6 /* TextField */, 3E79B19E2A55468500D36A26 /* Cell */, 0915C1732A5C4FEE00ACB8D4 /* StackView */, @@ -592,8 +757,8 @@ 097682D82A5C828E0008F4FB /* ImageView */, 3E79B19B2A54523400D36A26 /* Button */, 3EA2E1E12A53E880003516A3 /* Base */, - 3E79B1AF2A571B4B00D36A26 /* LineView.swift */, - 3EA2E1E82A541AEB003516A3 /* CustomNavigationBar.swift */, + DFB587BA2A5D588500704B6C /* DrawDashLineView.swift */, + 3E2EEF732A5C92980093BCA9 /* NavigationBar */, ); path = Common; sourceTree = ""; @@ -606,11 +771,69 @@ path = Base; sourceTree = ""; }; + 3ED40DFC2A608B2B00C7D5DA /* View */ = { + isa = PBXGroup; + children = ( + 3ED40DFF2A608B4600C7D5DA /* MyPageCollectionViewCell.swift */, + 3ED40E032A608EF000C7D5DA /* MyPageCollectionViewHeader.swift */, + 3ED40E052A60916000C7D5DA /* MyPagePurposeChipView.swift */, + 3E452B682A60FF7900EA456D /* MyPageCollectionViewFooter.swift */, + 3E452B6A2A614AAD00EA456D /* ImageWithSubtitleButton.swift */, + ); + path = View; + sourceTree = ""; + }; + 3ED40DFD2A608B2F00C7D5DA /* Model */ = { + isa = PBXGroup; + children = ( + 3E452B662A60E68000EA456D /* MyPageDTO.swift */, + ); + path = Model; + sourceTree = ""; + }; + 3EDC05BA2A5DEB7500EAD950 /* Filter */ = { + isa = PBXGroup; + children = ( + 3EDC05C82A5DF25900EAD950 /* Model */, + 3EDC05BC2A5DEB7F00EAD950 /* Views */, + 3EDC05BB2A5DEB7B00EAD950 /* ViewControllers */, + ); + path = Filter; + sourceTree = ""; + }; + 3EDC05BB2A5DEB7B00EAD950 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 3EDC05BD2A5DED4400EAD950 /* FilterPurposeViewController.swift */, + 3EDC05BF2A5DED9200EAD950 /* FilterBreadTypeViewController.swift */, + 3EDC05C12A5DED9D00EAD950 /* FilterIngredientViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; + 3EDC05BC2A5DEB7F00EAD950 /* Views */ = { + isa = PBXGroup; + children = ( + 3EDC05C32A5DEE3600EAD950 /* FilterCollectionViewCell.swift */, + ); + path = Views; + sourceTree = ""; + }; + 3EDC05C82A5DF25900EAD950 /* Model */ = { + isa = PBXGroup; + children = ( + 3ED40DFA2A5FD26200C7D5DA /* FilterDTO.swift */, + ); + path = Model; + sourceTree = ""; + }; DF959A672A56851000E75774 /* Enum */ = { isa = PBXGroup; children = ( 09C6264D2A5B29F8002C8110 /* SearchEnum.swift */, 09B13F572A59433500C0C723 /* TabBarItem.swift */, + 3ED40DF82A5FCF3800C7D5DA /* FilterEnum.swift */, + 3ED40E012A608B8600C7D5DA /* MyPageSectionEnum.swift */, ); path = Enum; sourceTree = ""; @@ -623,6 +846,14 @@ path = Colors; sourceTree = ""; }; + DFB587B72A5D580B00704B6C /* Onboarding */ = { + isa = PBXGroup; + children = ( + DFB587B82A5D584800704B6C /* OnboardingViewController.swift */, + ); + path = Onboarding; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -660,7 +891,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1420; - LastUpgradeCheck = 1420; + LastUpgradeCheck = 1430; TargetAttributes = { 098F32DC2A4200FD0092D09A = { CreatedOnToolsVersion = 14.2; @@ -755,70 +986,99 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3EFA97882A5EBCED002E70FA /* CustomNavigationBar.swift in Sources */, 0915C1702A5C3C5600ACB8D4 /* RegionStackView.swift in Sources */, 3E16E4FC2A57D24900B813D0 /* LikeModel.swift in Sources */, 3E79B1AE2A56F14A00D36A26 /* BakeryOverviewView.swift in Sources */, + 3ED40E042A608EF000C7D5DA /* MyPageCollectionViewHeader.swift in Sources */, 3E16E4FA2A57D1F400B813D0 /* RecommendationModel.swift in Sources */, + 3E452B6B2A614AAE00EA456D /* ImageWithSubtitleButton.swift in Sources */, 3E79B1A62A566F6400D36A26 /* WriteReviewViewController.swift in Sources */, + 097682E72A5CF0080008F4FB /* BakeryFilterView.swift in Sources */, + 3ED40DFB2A5FD26200C7D5DA /* FilterDTO.swift in Sources */, 090556292A51DBC000752067 /* UIView+.swift in Sources */, 090556292A51DBC000752067 /* UIView+.swift in Sources */, 090668FC2A4FF3C600F413FA /* AppDelegate.swift in Sources */, - 3E79B1B02A571B4B00D36A26 /* LineView.swift in Sources */, + 3EDC05C02A5DED9200EAD950 /* FilterBreadTypeViewController.swift in Sources */, + 09FD47C82A5DC991002020BD /* SearchNavigationView.swift in Sources */, 090668FD2A4FF3C600F413FA /* SceneDelegate.swift in Sources */, 097682DC2A5C83910008F4FB /* CGFloat+.swift in Sources */, + 3ED40DF92A5FCF3800C7D5DA /* FilterEnum.swift in Sources */, 0924632F2A5BA0F200B6F65D /* HomeBakeryCollectionViewCell.swift in Sources */, + 097682E92A5D0B6B0008F4FB /* BakeryListTopView.swift in Sources */, 09B13F5A2A5946B700C0C723 /* TabBar+.swift in Sources */, + 097682DF2A5C91280008F4FB /* BakeryListResponseDTO.swift in Sources */, 09B71C002A59D50900076AC2 /* HomeTopView.swift in Sources */, 090556492A51E7EC00752067 /* UIScreen+.swift in Sources */, 09B71C022A59D99200076AC2 /* SearchTextField.swift in Sources */, 09B13F482A5931DC00C0C723 /* TabBarController.swift in Sources */, + 3E2EEF752A5C93010093BCA9 /* BottomView.swift in Sources */, 0961C3642A501EBF0031A822 /* Strings.swift in Sources */, 093214082A5AE40900875EF6 /* SearchViewController.swift in Sources */, 09B13F542A593CD300C0C723 /* MyPageViewController.swift in Sources */, + 097682E32A5C95750008F4FB /* BakeryFilterCollectionViewCell.swift in Sources */, + 3EDC05C42A5DEE3600EAD950 /* FilterCollectionViewCell.swift in Sources */, 3E79B19D2A54523D00D36A26 /* BackButton.swift in Sources */, 09CA3EF82A5676AA0063897A /* CommonButton.swift in Sources */, 3EA2E1E32A53E88B003516A3 /* BaseViewController.swift in Sources */, 09C626582A5B4269002C8110 /* HomeHeaderView.swift in Sources */, 0915C1722A5C3D3700ACB8D4 /* PaddingLabel.swift in Sources */, + DFB587BB2A5D588500704B6C /* DrawDashLineView.swift in Sources */, 090556222A51DB3300752067 /* UIFont+.swift in Sources */, 09CA3EFC2A569E4A0063897A /* URLConstant.swift in Sources */, + 3EDC05BE2A5DED4400EAD950 /* FilterPurposeViewController.swift in Sources */, + 3EDC05C22A5DED9D00EAD950 /* FilterIngredientViewController.swift in Sources */, 09C6264E2A5B29F8002C8110 /* SearchEnum.swift in Sources */, 0915C16A2A5BFC5900ACB8D4 /* HomeBottomCollectionViewCell.swift in Sources */, 09CA3F082A569EBA0063897A /* Service.swift in Sources */, + 098716B82A6138BD00538D05 /* MyReviewsHeaderView.swift in Sources */, 09B13F4C2A593C6F00C0C723 /* HomeViewController.swift in Sources */, 09C626542A5B344B002C8110 /* HomeBestBakeryResponseDTO.swift in Sources */, 0915C16E2A5C2EE200ACB8D4 /* MarkStackView.swift in Sources */, 0905564B2A51E81D00752067 /* UIViewController+.swift in Sources */, 0915C1752A5C533900ACB8D4 /* HomeReviewCollectionViewCell.swift in Sources */, 097682DA2A5C829D0008F4FB /* GradientImageView.swift in Sources */, + 09FD47DD2A5E57F2002020BD /* MyReviewsViewController.swift in Sources */, 3EA2E1EB2A542151003516A3 /* SizeLiteral.swift in Sources */, + 3EFA97862A5EBCA9002E70FA /* LineView.swift in Sources */, 0987288D2A5BA1F000A29402 /* BookmarkButton.swift in Sources */, 090556202A51DB2C00752067 /* UIImage+.swift in Sources */, - 09B71BFC2A59CF3E00076AC2 /* Protocol.swift in Sources */, 0905562D2A51DBE700752067 /* UILabel+.swift in Sources */, 090556252A51DB9600752067 /* UICollectionView+.swift in Sources */, + 098716B62A60F52200538D05 /* BakeryTypeProtocol.swift in Sources */, 09B13F582A59433500C0C723 /* TabBarItem.swift in Sources */, 09B13F502A593CB800C0C723 /* BakeryListViewController.swift in Sources */, + 09FD47D92A5E5148002020BD /* MySavedBakeryViewController.swift in Sources */, 093214022A5AE27800875EF6 /* Utils.swift in Sources */, + 097682EB2A5D12BD0008F4FB /* BakeryListCollectionViewCell.swift in Sources */, 090556452A51E79B00752067 /* UITextField+.swift in Sources */, 3E16E5022A58176800B813D0 /* ReviewDetailTextView.swift in Sources */, 09CA3F062A569EB30063897A /* API.swift in Sources */, + DFB587B92A5D584800704B6C /* OnboardingViewController.swift in Sources */, DF959A6E2A568C9400E75774 /* UIColor+.swift in Sources */, + 3E452B672A60E68000EA456D /* MyPageDTO.swift in Sources */, 09C626562A5B3572002C8110 /* HomeBestReviewResponseDTO.swift in Sources */, 090556472A51E7D900752067 /* UITextView+.swift in Sources */, + 3EDC05D02A5E4FF900EAD950 /* String+.swift in Sources */, 0905562B2A51DBCE00752067 /* UIStackView+.swift in Sources */, 090556312A51DDD800752067 /* UITableView+.swift in Sources */, 09B13F562A59410C00C0C723 /* CALayer+.swift in Sources */, - 3EA2E1E92A541AEB003516A3 /* CustomNavigationBar.swift in Sources */, 090556312A51DDD800752067 /* UITableView+.swift in Sources */, 3E16E4FE2A57F06D00B813D0 /* OptionsCollectionViewFlowLayout.swift in Sources */, 3E79B1B22A571E1F00D36A26 /* OptionsCollectionView.swift in Sources */, 3E79B1B52A571E7100D36A26 /* OptionsCollectionViewCell.swift in Sources */, 090556312A51DDD800752067 /* UITableView+.swift in Sources */, + 3ED40E002A608B4700C7D5DA /* MyPageCollectionViewCell.swift in Sources */, + 097682E52A5C99340008F4FB /* BakeryFilterItems.swift in Sources */, + 098716B42A6061F500538D05 /* SearchResponseDTO.swift in Sources */, + 09FD47D22A5DF892002020BD /* EmptyCollectionViewCell.swift in Sources */, + 3ED40E062A60916000C7D5DA /* MyPagePurposeChipView.swift in Sources */, 090556272A51DBB100752067 /* NSObject+.swift in Sources */, 3E79B1A02A5546E700D36A26 /* DescriptionCollectionViewCell.swift in Sources */, - 3E16E5002A57FFD900B813D0 /* OptionsCollectionViewHeader.swift in Sources */, + 3ED40E022A608B8600C7D5DA /* MyPageSectionEnum.swift in Sources */, + 3E452B692A60FF7900EA456D /* MyPageCollectionViewFooter.swift in Sources */, 3E16E4F52A57CF3800B813D0 /* WriteReviewDTO.swift in Sources */, + 09FD47CA2A5DD38D002020BD /* SearchResultView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -963,7 +1223,7 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "GEON-PPANG-iOS/Resource/Info.plist"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen.storyboard; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( @@ -992,7 +1252,7 @@ GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "GEON-PPANG-iOS/Resource/Info.plist"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen.storyboard; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/GEON-PPANG-iOS.xcodeproj/xcshareddata/xcschemes/GEON-PPANG-iOS.xcscheme b/GEON-PPANG-iOS.xcodeproj/xcshareddata/xcschemes/GEON-PPANG-iOS.xcscheme index 05ff9a2a..58878658 100644 --- a/GEON-PPANG-iOS.xcodeproj/xcshareddata/xcschemes/GEON-PPANG-iOS.xcscheme +++ b/GEON-PPANG-iOS.xcodeproj/xcshareddata/xcschemes/GEON-PPANG-iOS.xcscheme @@ -1,6 +1,6 @@ Bool { FirebaseApp.configure() + sleep(UInt32(1.5)) + return true } diff --git a/GEON-PPANG-iOS/Application/SceneDelegate.swift b/GEON-PPANG-iOS/Application/SceneDelegate.swift index 5a66dd4c..03e62fd0 100644 --- a/GEON-PPANG-iOS/Application/SceneDelegate.swift +++ b/GEON-PPANG-iOS/Application/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let window = UIWindow(windowScene: windowScene) window.overrideUserInterfaceStyle = UIUserInterfaceStyle.light - let rootViewController = TabBarController() + let rootViewController = WriteReviewViewController() let navigationController = UINavigationController(rootViewController: rootViewController) navigationController.isNavigationBarHidden = true window.rootViewController = navigationController diff --git a/GEON-PPANG-iOS/Global/Enum/FilterEnum.swift b/GEON-PPANG-iOS/Global/Enum/FilterEnum.swift new file mode 100644 index 00000000..1b984e9b --- /dev/null +++ b/GEON-PPANG-iOS/Global/Enum/FilterEnum.swift @@ -0,0 +1,77 @@ +// +// FilterEnum.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/13. +// + +import Foundation + +enum FilterType { + case purpose + case breadType + case ingredient + + var cellSize: CGSize { + switch self { + case .purpose, .ingredient: + return .init(width: CGFloat().convertByWidthRatio(327), + height: CGFloat().convertByHeightRatio(106)) + case .breadType: + return .init(width: CGFloat().convertByWidthRatio(153), + height: CGFloat().convertByHeightRatio(161)) + } + } + + var labelSpacing: CGFloat { + switch self { + case .purpose: return 9 + case .breadType: return 24 + case .ingredient: return 0 + } + } +} + +enum FilterPurposeType: String, CaseIterable { + case health = "건강 · 체질" + case taste = "맛 · 다이어트" + case vegan = "비건 · 채식지향" + + var description: String { + switch self { + case .health: return "아토피 , 알레르기 , 암 , 당뇨 , 소화문제" + case .taste: return "그냥 맛있어서! 식이 관리를 위해" + case .vegan: return "종교 , 환경 , 동물 , 노동권을 위한 비거니즘" + } + } + + var data: String { + switch self { + case .health: return "HEALTH" + case .taste: return "TASTE" + case .vegan: return "VEGAN" + } + } +} + +enum FilterBreadType: String, CaseIterable { + case isGlutenFree = "글루텐프리" + case isVegan = "비건빵" + case isNutFree = "넛프리" + case isSugarless = "저당, 무설탕" + + var description: String { + switch self { + case .isGlutenFree: return "NO 글루텐 포함\n밀 , 곡물류" + case .isVegan: return "NO 동물성재료\n(유제품, 계란)" + case .isNutFree: return "NO 견과류" + case .isSugarless: return "대체당 사용" + } + } +} + +enum FilterIngredientType: String, CaseIterable { + case isNutrientOpen = "영양성분 공개" + case isIngredientOpen = "원재료 공개" + case isNotOpen = "비공개" +} diff --git a/GEON-PPANG-iOS/Global/Enum/MyPageSectionEnum.swift b/GEON-PPANG-iOS/Global/Enum/MyPageSectionEnum.swift new file mode 100644 index 00000000..6138ea14 --- /dev/null +++ b/GEON-PPANG-iOS/Global/Enum/MyPageSectionEnum.swift @@ -0,0 +1,22 @@ +// +// MyPageEnum.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import Foundation + +enum MyPageSectionEnum: String, CaseIterable { + + case terms + case questions + + var items: [String] { + switch self { + case .terms: return ["이용약관"] + case .questions: return ["자주 묻는 질문", "문의하기"] + } + } + +} diff --git a/GEON-PPANG-iOS/Global/Extensions/Design/UIColor+.swift b/GEON-PPANG-iOS/Global/Extensions/Design/UIColor+.swift index c1dfd1b1..8451c597 100644 --- a/GEON-PPANG-iOS/Global/Extensions/Design/UIColor+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/Design/UIColor+.swift @@ -25,7 +25,7 @@ extension UIColor { static let gbbBackground1 = UIColor(named: "Background_1") static let gbbBackground2 = UIColor(named: "Background_2") - static let gbbGray50 = UIColor(named: "gray-50") + static let gbbWhite = UIColor(named: "white") static let gbbGray100 = UIColor(named: "gray-100") static let gbbGray200 = UIColor(named: "gray-200") static let gbbGray300 = UIColor(named: "gray-300") @@ -33,5 +33,5 @@ extension UIColor { static let gbbGray500 = UIColor(named: "gray-500") static let gbbGray600 = UIColor(named: "gray-600") static let gbbGray700 = UIColor(named: "gray-700") - static let gbbGray800 = UIColor(named: "gray-800") + static let gbbBlack = UIColor(named: "black") } diff --git a/GEON-PPANG-iOS/Global/Extensions/Design/UIImage+.swift b/GEON-PPANG-iOS/Global/Extensions/Design/UIImage+.swift index b2fb163d..9eac6b2e 100644 --- a/GEON-PPANG-iOS/Global/Extensions/Design/UIImage+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/Design/UIImage+.swift @@ -6,7 +6,6 @@ // import UIKit - extension UIImage { // 따로 픽셀이 적혀있지 않은 컴포넌트는 24px임. @@ -44,10 +43,12 @@ extension UIImage { static let deleteIcon = UIImage(named: "ic_delete")!.withRenderingMode(.alwaysOriginal) static let dotdotdotIcon = UIImage(named: "ic_dotdotdot")!.withRenderingMode(.alwaysOriginal) static let hideIcon = UIImage(named: "ic_hide")!.withRenderingMode(.alwaysOriginal) + static let launchscreenIcon = UIImage(named: "ic_launchscreen")!.withRenderingMode(.alwaysOriginal) static let linkIcon = UIImage(named: "ic_link")!.withRenderingMode(.alwaysOriginal) - static let listIcon = UIImage(named: "ic_link")!.withRenderingMode(.alwaysOriginal) /// 건빵집 리스트 옆에 얘 써야 됨, 탭바의 storelist 아님! + static let listIcon = UIImage(named: "ic_list")!.withRenderingMode(.alwaysOriginal) // 건빵집 리스트 옆에 얘 써야 됨, 탭바의 storelist 아님! static let logoIcon16px = UIImage(named: "ic_logo_16px")!.withRenderingMode(.alwaysOriginal) static let noticeIcon18px = UIImage(named: "ic_notice_18px")!.withRenderingMode(.alwaysOriginal) + static let profileIcon = UIImage(named: "ic_profile")!.withRenderingMode(.alwaysOriginal) static let reviewIcon = UIImage(named: "ic_review")!.withRenderingMode(.alwaysOriginal) static let searchIcon400 = UIImage(named: "ic_search_400")!.withRenderingMode(.alwaysOriginal) /// gray400 static let searchIcon600 = UIImage(named: "ic_search_600")!.withRenderingMode(.alwaysOriginal) /// gray600 diff --git a/GEON-PPANG-iOS/Global/Extensions/UI/CGFloat+.swift b/GEON-PPANG-iOS/Global/Extensions/UI/CGFloat+.swift index 5b54075e..490df32a 100644 --- a/GEON-PPANG-iOS/Global/Extensions/UI/CGFloat+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/UI/CGFloat+.swift @@ -21,6 +21,14 @@ extension CGFloat { return UIScreen.main.hasNotch ? hasNotch : noNotch } + func heightConsideringNotch(_ height: CGFloat) -> CGFloat { + return UIScreen.main.hasNotch ? height : height - 24 + } + + func heightConsideringBottomSafeArea(_ height: CGFloat) -> CGFloat { + return UIScreen.main.hasNotch ? height : height - 34 + } + /// 아이폰 13 미니(width 375)를 기준으로 레이아웃을 잡고, 기기의 width 사이즈를 곱해 대응 값을 구할 때 사용 func convertByWidthRatio(_ convert: CGFloat) -> CGFloat { return (convert / 375) * getDeviceWidth() diff --git a/GEON-PPANG-iOS/Global/Extensions/UI/String+.swift b/GEON-PPANG-iOS/Global/Extensions/UI/String+.swift new file mode 100644 index 00000000..8853f144 --- /dev/null +++ b/GEON-PPANG-iOS/Global/Extensions/UI/String+.swift @@ -0,0 +1,19 @@ +// +// String+.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/12. +// + +import Foundation + +extension String { + + func insertString(_ string: String, at index: Int) -> String { + var newString = self + let stringIndex = self.index(self.startIndex, offsetBy: index) + newString.insert(contentsOf: string, at: stringIndex) + return newString + } + +} diff --git a/GEON-PPANG-iOS/Global/Extensions/UI/UILabel+.swift b/GEON-PPANG-iOS/Global/Extensions/UI/UILabel+.swift index 4ee204f7..1a8f6edb 100644 --- a/GEON-PPANG-iOS/Global/Extensions/UI/UILabel+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/UI/UILabel+.swift @@ -47,6 +47,13 @@ extension UILabel { } self.attributedText = attributedString } + + func setLineHeight(by multiple: CGFloat, with text: String) { + var paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineHeightMultiple = multiple + attributedText = NSMutableAttributedString(string: text, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle]) + } + } extension UILabel { diff --git a/GEON-PPANG-iOS/Global/Extensions/UI/UIScreen+.swift b/GEON-PPANG-iOS/Global/Extensions/UI/UIScreen+.swift index 0f34efec..495e321e 100644 --- a/GEON-PPANG-iOS/Global/Extensions/UI/UIScreen+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/UI/UIScreen+.swift @@ -9,6 +9,6 @@ import UIKit extension UIScreen { var hasNotch: Bool { - return !( (UIScreen.main.bounds.width / UIScreen.main.bounds.height) > 0.5 ) + return (UIScreen.main.bounds.width / UIScreen.main.bounds.height) < 0.5 } } diff --git a/GEON-PPANG-iOS/Global/Extensions/UI/UIView+.swift b/GEON-PPANG-iOS/Global/Extensions/UI/UIView+.swift index 0ed7644d..2b92f5f8 100644 --- a/GEON-PPANG-iOS/Global/Extensions/UI/UIView+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/UI/UIView+.swift @@ -18,7 +18,7 @@ extension UIView { layer.masksToBounds = true } - func makeBorder(width: CGFloat, color: UIColor ) { + func makeBorder(width: CGFloat, color: UIColor) { layer.borderWidth = width layer.borderColor = color.cgColor } diff --git a/GEON-PPANG-iOS/Global/Extensions/UI/UIViewController+.swift b/GEON-PPANG-iOS/Global/Extensions/UI/UIViewController+.swift index 40ff2d6d..c56bedbf 100644 --- a/GEON-PPANG-iOS/Global/Extensions/UI/UIViewController+.swift +++ b/GEON-PPANG-iOS/Global/Extensions/UI/UIViewController+.swift @@ -31,6 +31,13 @@ extension UIViewController { return (convert / 812) * getDeviceHeight() } + func popViewControllerAction() -> UIAction { + let action = UIAction { [weak self] _ in + self?.navigationController?.popViewController(animated: true) + } + return action + } + func setKeyboardHideGesture() { let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard)) @@ -38,7 +45,35 @@ extension UIViewController { view.addGestureRecognizer(tap) } - @objc func dismissKeyboard() { + func setKeyboardNotificationCenter() { + NotificationCenter.default.addObserver(self, selector: #selector(moveUpAboutKeyboard), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(moveDownAboutKeyboard), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + func setNavigationBarHidden() { + self.navigationController?.navigationBar.isHidden = true + } + + // MARK: - objc functions + + @objc + func dismissKeyboard() { view.endEditing(true) } + + @objc + func moveUpAboutKeyboard(_ notification: NSNotification) { + if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { + UIView.animate(withDuration: 0.2, animations: { + self.view.transform = CGAffineTransform(translationX: 0, y: -keyboardSize.height + 24) + }) + } + } + + @objc + func moveDownAboutKeyboard(_ notification: NSNotification) { + UIView.animate(withDuration: 0.2, animations: { + self.view.transform = .identity + }) + } } diff --git a/GEON-PPANG-iOS/Global/Literals/Strings.swift b/GEON-PPANG-iOS/Global/Literals/Strings.swift index 42c2c063..65ac2792 100644 --- a/GEON-PPANG-iOS/Global/Literals/Strings.swift +++ b/GEON-PPANG-iOS/Global/Literals/Strings.swift @@ -18,6 +18,12 @@ struct I18N { """ } + /// Onboarding + struct Onboarding { + static let latelySigninText = "최근에 카카오톡으로 로그인 했습니다." + static let latelySigninSNS = "카카오톡" + } + /// WriteReviewViewController struct WriteReview { static let likeOptionTitle = "건빵집은 어떠셨나요?" @@ -33,4 +39,45 @@ struct I18N { 제재를 받을 수 있습니다. 또한, 건빵은 이에 대한 책임을 지지 않습니다. """ } + + /// BakeryList + struct BakeryList { + static let bakeryTitle = "건빵집 리스트" + static let glutenfree = "글루텐프리" + static let nutfree = "넛프리" + static let vegan = "비건빵" + static let noSugar = "저당, 무설탕" + static let defaultFilter = "기본순" + } + + /// Filter Selection + struct Filter { + static let purposeTitle = "반가워요 님 :)\n건빵을 찾은 이유를 알려주세요!" + static let breadTypeTitle = "어떤 빵을 원하시나요?" + static let breadTypeSubtitle = "중복선택이 가능해요!" + static let ingredientTitle = "어떤 성분을 원하시나요?" + static let ingredientSubtitle = "중복선택이 가능해요!" + } + + /// MyPage + struct MyPage { + static let title = "마이페이지" + static let bookmark = "저장목록" + static let myReviews = "내가 쓴 리뷰" + static let terms = "이용약관" + static let commonQuestions = "자주 묻는 질문" + static let askQuestions = "문의하기" + static let appVersion = "앱버전" + static let appVersionNum = "v 0.0.1" + } + + /// MySavedBakery + struct MySavedBakery { + static let naviTitle = "저장 목록" + } + + /// MyReviews + struct MyReviews { + static let naviTitle = "내가 쓴 리뷰" + } } diff --git a/GEON-PPANG-iOS/Global/Protocol/BakeryTypeProtocol.swift b/GEON-PPANG-iOS/Global/Protocol/BakeryTypeProtocol.swift new file mode 100644 index 00000000..d47f1778 --- /dev/null +++ b/GEON-PPANG-iOS/Global/Protocol/BakeryTypeProtocol.swift @@ -0,0 +1,30 @@ +// +// BakeryTypeProtocol.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/14. +// + +import Foundation + +protocol BakeryTypeProtocol { + var isGlutenFree: Bool { get } + var isNutFree: Bool { get } + var isVegan: Bool { get } + var isSugarFree: Bool { get } +} + +protocol BakeryListProtocol { + var bakeryName: String { get } + var bookmarkCount: Int { get } + var isBooked: Bool { get } + + var isHACCP: Bool { get } + var isVegan: Bool { get } + var isNonGMO: Bool { get } + + var firstNearStation: String { get } + var secondNearStation: String? { get } + + var breadType: BreadResponseType { get } +} diff --git a/GEON-PPANG-iOS/Global/Protocol/Protocol.swift b/GEON-PPANG-iOS/Global/Protocol/Protocol.swift deleted file mode 100644 index 924a7429..00000000 --- a/GEON-PPANG-iOS/Global/Protocol/Protocol.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// Protocol.swift -// GEON-PPANG-iOS -// -// Created by JEONGEUN KIM on 2023/07/09. -// - -import Foundation diff --git a/GEON-PPANG-iOS/Global/Utils/Utils.swift b/GEON-PPANG-iOS/Global/Utils/Utils.swift index be4265da..538d0f43 100644 --- a/GEON-PPANG-iOS/Global/Utils/Utils.swift +++ b/GEON-PPANG-iOS/Global/Utils/Utils.swift @@ -21,4 +21,17 @@ final class Utils { modalViewController.modalPresentationStyle = modalStyle viewController.present(modalViewController, animated: false) } + + class func updateCollectionViewConstraint(of collectionView: UICollectionView) { + let height = collectionView.collectionViewLayout.collectionViewContentSize.height + guard height != 0 else { return } + collectionView.snp.updateConstraints { + $0.height.equalTo(height) + } + } + + class func calculateCollectionViewSize(of collectionView: UICollectionView) -> CGSize { + let size = collectionView.collectionViewLayout.collectionViewContentSize + return size + } } diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryFilterCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryFilterCollectionViewCell.swift new file mode 100644 index 00000000..c479a620 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryFilterCollectionViewCell.swift @@ -0,0 +1,89 @@ +// +// BakeryFilterCollectionViewCell.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/11. +// + +import UIKit + +import SnapKit +import Then + +final class BakeryFilterCollectionViewCell: UICollectionViewCell { + + // MARK: - Property + + private var isTapped: Bool = true { + didSet { + updateUI(isTapped) + } + } + + // MARK: - UI Property + + private let hStackView = UIStackView() + private let iconView = UIImageView() + var filterTitle = UILabel() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setUI() { + contentView.do { + $0.makeCornerRound(radius: 18) + $0.makeBorder(width: 1, color: .gbbGray200!) + $0.backgroundColor = .gbbGray100 + } + hStackView.do { + $0.axis = .horizontal + $0.spacing = 4 + $0.distribution = .fillProportionally + } + filterTitle.do { + $0.font = .captionM1 + $0.sizeToFit() + } + } + + private func setLayout() { + contentView.addSubview(hStackView) + hStackView.addArrangedSubviews(iconView, filterTitle) + + hStackView.snp.makeConstraints { + $0.edges.equalToSuperview().inset(UIEdgeInsets(top: 8, left: 12, bottom: 8, right: 12)) + } + + iconView.snp.makeConstraints { + $0.size.equalTo(20) + } + } + + func bind(item: BakeryFilterItems, index: Int) { + iconView.image = item.leftIcon + filterTitle.text = item.filter.title + isTapped = item.status == .on ? false : true + } + + func updateUI(_ isTapped: Bool) { + filterTitle.textColor = isTapped ? .gbbGray700 : .gbbBackground2 + contentView.backgroundColor = isTapped ? .gbbGray100 : .gbbMain3 + contentView.makeBorder(width: 1, color: isTapped ? .gbbGray200! : .gbbMain2!) + } + + func getSize() { + return filterTitle.sizeToFit() + } +} diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListCollectionViewCell.swift new file mode 100644 index 00000000..565c93f9 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListCollectionViewCell.swift @@ -0,0 +1,250 @@ +// +// BakeryListCollectionViewCell.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/11. +// + +import UIKit + +enum BakeryViewType { + case defaultType + case reviewType +} + +final class BakeryListCollectionViewCell: UICollectionViewCell { + + // MARK: - Property + + var index = 0 + var updateData: ((Bool, Int) -> Void)? + private var breadTypeTag: [String] = [] + private var ingredientList: [BakeryListResponseDTO] = BakeryListResponseDTO.item + private var bakeryViewType: BakeryViewType? = .defaultType { + didSet { + configure() + } + } + + // MARK: - UI Property + + private let markStackView = MarkStackView() + private let bakeryImage = UIImageView() + private let bakeryTitle = UILabel() + private let regionStackView = RegionStackView() + private lazy var bookMarkButton = BookmarkButton(configuration: .plain()) + private lazy var arrowButton = UIButton() + private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: OptionsCollectionViewFlowLayout()) + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + setRegistration() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setUI() { + self.do { + $0.contentView.backgroundColor = .white + } + bakeryImage.do { + $0.backgroundColor = .darkGray + $0.makeCornerRound(radius: 5) + } + markStackView.do { + $0.getIconImage(.smallHACCPMark, .smallVeganMark, .smallGMOMark) + } + bakeryTitle.do { + $0.basic(font: .pretendardBold(20), color: .gbbGray700!) + } + collectionView.do { + $0.isScrollEnabled = false + $0.backgroundColor = .clear + $0.delegate = self + $0.dataSource = self + } + } + + private func setLayout() { + contentView.addSubviews(bakeryImage, bakeryTitle, collectionView, regionStackView) + bakeryImage.addSubview(markStackView) + + bakeryImage.snp.makeConstraints { + $0.size.equalTo(90) + $0.top.equalToSuperview().offset(24) + $0.leading.equalToSuperview().inset(48) + } + + markStackView.snp.makeConstraints { + $0.top.leading.equalToSuperview().offset(8) + $0.width.equalTo(55) + $0.height.equalTo(22) + } + + bakeryTitle.snp.makeConstraints { + $0.top.equalTo(bakeryImage.snp.top) + $0.leading.equalTo(bakeryImage.snp.trailing).offset(14) + } + + collectionView.snp.makeConstraints { + $0.height.equalTo(25) + $0.top.equalTo(bakeryTitle.snp.bottom).offset(16) + $0.leading.equalTo(bakeryImage.snp.trailing).offset(14) + $0.trailing.equalToSuperview().offset(-70) + } + + regionStackView.snp.makeConstraints { + $0.top.equalTo(collectionView.snp.bottom).offset(16) + $0.height.equalTo(29) + $0.leading.equalTo(bakeryImage.snp.trailing).offset(14) + $0.bottom.equalToSuperview().offset(-24) + } + } + + func updateUI(data: T, index: Int) { + self.index = index + bakeryTitle.text = data.bakeryName + bookMarkButton.getCount(data.bookmarkCount) + bookMarkButton.updateData = { [weak self] status in + guard let self = self else { return } + self.updateData?(status, self.index) + } + bookMarkButton.isSelected = data.isBooked + markStackView.getMarkStatus(data.isHACCP, data.isVegan, data.isNonGMO) + if data.secondNearStation == "" { + regionStackView.removeSecondRegion() + } + regionStackView.getRegionName(data.firstNearStation, data.secondNearStation ?? "") + + if data.breadType.isGlutenFree { + breadTypeTag.append(I18N.BakeryList.glutenfree) + } + + if data.breadType.isNutFree { + breadTypeTag.append(I18N.BakeryList.nutfree) + } + + if data.breadType.isVegan { + breadTypeTag.append(I18N.BakeryList.vegan) + } + + if data.breadType.isSugarFree { + breadTypeTag.append(I18N.BakeryList.noSugar) + } + + collectionView.snp.remakeConstraints { + $0.height.equalTo(getHeight(breadTypeTag)) + $0.top.equalTo(bakeryTitle.snp.bottom).offset(10) + $0.leading.equalTo(bakeryImage.snp.trailing).offset(14) + $0.trailing.equalToSuperview().offset(-70) + } + } + + func getViewType(_ type: BakeryViewType) { + bakeryViewType = type + } + + func defaultViewButton() { + addSubview(bookMarkButton) + + bookMarkButton.do { + $0.configuration?.imagePlacement = NSDirectionalRectEdge.top + $0.configuration?.imagePadding = 4 + $0.configuration?.contentInsets = .zero + $0.addAction(UIAction { _ in + print("default") + }, for: .touchUpInside) + } + + bookMarkButton.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.trailing.equalToSuperview().offset(-24) + $0.size.equalTo(34) + } + } + + func getHeight(_ list: [String]) -> CGFloat { + var width: CGFloat = 0 + list.forEach { + width += $0.size(withAttributes: [NSAttributedString.Key.font: UIFont.pretendardMedium(13)]).width + 4 + } + width -= 4 + + return width < (UIScreen.main.bounds.width - 206) ? 25 : 60 + } + + func reviewViewButton() { + addSubview(arrowButton) + + regionStackView.getBackgroundColor(.gbbGray700!) + arrowButton.do { + $0.setImage(.rightArrowIcon, for: .normal) + $0.addAction(UIAction { _ in + print("review") + }, for: .touchUpInside) + } + + bakeryImage.snp.remakeConstraints { + $0.size.equalTo(90) + $0.top.equalToSuperview().offset(10) + $0.leading.equalToSuperview().offset(24) + } + arrowButton.snp.makeConstraints { + $0.centerY.equalToSuperview() + $0.trailing.equalToSuperview().offset(-12) + $0.size.equalTo(23) + } + } + + func configure() { + switch bakeryViewType { + case .defaultType: + defaultViewButton() + case .reviewType: + reviewViewButton() + case .none: + return + } + } +} + +// MARK: - CollectionView Register + +extension BakeryListCollectionViewCell { + private func setRegistration() { + collectionView.register(cell: DescriptionCollectionViewCell.self) + } +} + +// MARK: - UICollectionViewDataSource + +extension BakeryListCollectionViewCell: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return breadTypeTag.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: DescriptionCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.configureTagTitle(self.breadTypeTag[indexPath.item]) + return cell + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension BakeryListCollectionViewCell: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let tagTitle = self.breadTypeTag[indexPath.item] + let itemSize = tagTitle.size(withAttributes: [NSAttributedString.Key.font: UIFont.pretendardMedium(13)]) + return CGSize(width: itemSize.width + 12, height: itemSize.height + 8) + } +} diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListViewController.swift b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListViewController.swift index ef60a958..22539736 100644 --- a/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListViewController.swift +++ b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/BakeryListViewController.swift @@ -7,10 +7,93 @@ import UIKit -final class BakeryListViewController: UIViewController { +import SnapKit +import Then +final class BakeryListViewController: BaseViewController { + + // MARK: - Property + + enum Section { + case main + } + typealias DataSource = UICollectionViewDiffableDataSource + private var dataSource: DataSource? + private var filterlist: [BakeryListResponseDTO] = BakeryListResponseDTO.item + private lazy var safeArea = self.view.safeAreaLayoutGuide + + // MARK: - UI Property + + private let bakeryTopView = BakeryListTopView() + private let bakeryFilterView = BakeryFilterView() + private lazy var bakeryListCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + + // MARK: - Life Cycle + override func viewDidLoad() { super.viewDidLoad() - + + setRegistration() + setupDataSource() + setReloadData() + + } + + override func setUI() { + bakeryFilterView.do { + $0.backgroundColor = .clear + } + } + + override func setLayout() { + view.addSubviews(bakeryTopView, bakeryFilterView, bakeryListCollectionView) + + bakeryTopView.snp.makeConstraints { + $0.top.equalTo(safeArea) + $0.directionalHorizontalEdges.equalTo(safeArea) + $0.height.equalTo(91) + } + + bakeryFilterView.snp.makeConstraints { + $0.top.equalTo(bakeryTopView.snp.bottom) + $0.directionalHorizontalEdges.equalTo(safeArea) + $0.height.equalTo(convertByHeightRatio(72)) + } + + bakeryListCollectionView.snp.makeConstraints { + $0.top.equalTo(bakeryFilterView.snp.bottom) + $0.leading.equalTo(safeArea).offset(-24) + $0.trailing.equalTo(safeArea) + $0.bottom.equalToSuperview() + } + } + + private func setRegistration() { + bakeryListCollectionView.register(cell: BakeryListCollectionViewCell.self) + } + + private func layout() -> UICollectionViewLayout { + var config = UICollectionLayoutListConfiguration(appearance: .plain) + config.backgroundColor = .clear + config.showsSeparators = true + + let layout = UICollectionViewCompositionalLayout.list(using: config) + return layout + } + + private func setupDataSource() { + dataSource = DataSource(collectionView: bakeryListCollectionView, cellProvider: { collectionView, indexPath, item in + let cell: BakeryListCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.defaultType) + cell.updateUI(data: item, index: indexPath.item) + return cell + }) + } + + private func setReloadData() { + var snapshot = NSDiffableDataSourceSnapshot() + defer { dataSource?.apply(snapshot, animatingDifferences: false)} + snapshot.appendSections([.main]) + snapshot.appendItems(filterlist) } } diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/Model/BakeryFilterItems.swift b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/Model/BakeryFilterItems.swift new file mode 100644 index 00000000..ba17ac8a --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/Model/BakeryFilterItems.swift @@ -0,0 +1,71 @@ +// +// BakeryFilterItems.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/11. +// + +import UIKit + +enum Status { + case on, off +} + +enum Filter { + case HARD + case DESSERT + case BRUNCH + + var title: String { + switch self { + case .HARD: + return "하드빵류" + case .DESSERT: + return "디저트류" + case .BRUNCH: + return "브런치류" + } + } +} + +struct BakeryFilterItems: Hashable { + var identifier = UUID() + var filter: Filter + var status: Status + var leftIcon: UIImage + static var item: [BakeryFilterItems] = [BakeryFilterItems(filter: .HARD, status: .off, leftIcon: .disabledHardIcon), + BakeryFilterItems(filter: .DESSERT, status: .off, leftIcon: .disabledCakeIcon), + BakeryFilterItems(filter: .BRUNCH, status: .off, leftIcon: .disabledSandwichIcon) + ] + + public func isSelected() -> Self { + switch self.status { + case .off: + switch self.filter { + case .HARD: + return BakeryFilterItems(filter: filter, status: status, leftIcon: .disabledHardIcon) + case .DESSERT: + return BakeryFilterItems(filter: filter, status: .off, leftIcon: .disabledCakeIcon) + case .BRUNCH: + return BakeryFilterItems(filter: filter, status: .off, leftIcon: .disabledSandwichIcon) + } + + case .on: + switch self.filter { + case .HARD: + return BakeryFilterItems(filter: filter, status: status, leftIcon: .enabledHardIcon) + case .DESSERT: + return BakeryFilterItems(filter: filter, status: status, leftIcon: .enabledCakeIcon) + case .BRUNCH: + return BakeryFilterItems(filter: filter, status: status, leftIcon: .enabledSandwichIcon) + } + } + } + + func hash(into hasher: inout Hasher) { + hasher.combine(identifier) + } + static func == (lhs: BakeryFilterItems, rhs: BakeryFilterItems) -> Bool { + return lhs.identifier == rhs.identifier + } +} diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/Model/BakeryListResponseDTO.swift b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/Model/BakeryListResponseDTO.swift new file mode 100644 index 00000000..0095da3b --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/BakeryList/ViewControllers/Model/BakeryListResponseDTO.swift @@ -0,0 +1,55 @@ +// +// BakeryListResponseDTO.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/11. +// + +// MARK: - BakeryListResponseDTO + +struct BakeryListResponseDTO: Hashable, BakeryListProtocol { + let bakeryID: Int + let bakeryName: String + let bakeryPicture: String + let isHACCP: Bool + let isVegan: Bool + let isNonGMO: Bool + let breadType: BreadResponseType + let firstNearStation: String + let secondNearStation: String? + let isBooked: Bool + let bookmarkCount: Int +} + +// MARK: - BreadType + +struct BreadResponseType: Codable, Hashable { + let breadTypeID: Int + let breadTypeName: String + let isGlutenFree: Bool + let isVegan: Bool + let isNutFree: Bool + let isSugarFree: Bool + + func configureTrueOptions() -> [(String, Bool)] { + var optionsBoolArray: [(String, Bool)] = [] + if isGlutenFree { optionsBoolArray.append((I18N.BakeryList.glutenfree, true)) } + if isVegan { optionsBoolArray.append((I18N.BakeryList.vegan, true)) } + if isNutFree { optionsBoolArray.append((I18N.BakeryList.nutfree, true)) } + if isSugarFree { optionsBoolArray.append((I18N.BakeryList.noSugar, true)) } + return optionsBoolArray + } +} + +extension BakeryListResponseDTO { + static let item: [BakeryListResponseDTO] = [BakeryListResponseDTO(bakeryID: 1, bakeryName: "건대 초코빵", bakeryPicture: "ursl", isHACCP: true, isVegan: true, isNonGMO: true, breadType: .breadItem1, firstNearStation: "건대역", secondNearStation: "건대", isBooked: true, bookmarkCount: 7), + BakeryListResponseDTO(bakeryID: 2, bakeryName: "건대 초코빵", bakeryPicture: "ursl", isHACCP: true, isVegan: true, isNonGMO: true, breadType: .breadItem2, firstNearStation: "건대역", secondNearStation: "건대", isBooked: true, bookmarkCount: 7), + BakeryListResponseDTO(bakeryID: 3, bakeryName: "건대 초코빵", bakeryPicture: "ursl", isHACCP: true, isVegan: true, isNonGMO: true, breadType: .breadItem3, firstNearStation: "건대역", secondNearStation: "건대", isBooked: true, bookmarkCount: 7) + ] +} +extension BreadResponseType { + static let breadItem1: BreadResponseType = BreadResponseType(breadTypeID: 1, breadTypeName: "글루텐프리", isGlutenFree: true, isVegan: true, isNutFree: true, isSugarFree: true) + static let breadItem2: BreadResponseType = BreadResponseType(breadTypeID: 1, breadTypeName: "글루텐프리", isGlutenFree: true, isVegan: false, isNutFree: false, isSugarFree: true) + static let breadItem3: BreadResponseType = BreadResponseType(breadTypeID: 1, breadTypeName: "글루텐프리", isGlutenFree: true, isVegan: true, isNutFree: true, isSugarFree: true) + +} diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/Views/BakeryFilterView.swift b/GEON-PPANG-iOS/Presentation/BakeryList/Views/BakeryFilterView.swift new file mode 100644 index 00000000..199029ac --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/BakeryList/Views/BakeryFilterView.swift @@ -0,0 +1,169 @@ +// +// BakeryFilterView.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/11. +// + +import UIKit + +import SnapKit +import Then + +final class BakeryFilterView: UIView { + + // MARK: - Property + + enum Section: Hashable { + case main + } + + typealias DataSource = UICollectionViewDiffableDataSource + private var dataSource: DataSource? + private var filterlist: [BakeryFilterItems] = BakeryFilterItems.item + + // MARK: - UI Property + + private lazy var filterCollectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + private lazy var filterButton = UIButton(configuration: .plain()) + + private let topView = UIView() + private let lineView = UIView() + private let bottomView = UIView() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + + setRegistration() + setDataSource() + setReloadData() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setUI() { + filterCollectionView.do { + $0.delegate = self + $0.showsHorizontalScrollIndicator = false + } + + filterButton.do { + $0.configuration?.background.strokeWidth = 1 + $0.configuration?.background.strokeColor = .gbbGray200 + $0.configuration?.baseForegroundColor = .black + $0.configuration?.image = .swapIcon + $0.configuration?.attributedTitle = AttributedString(I18N.BakeryList.defaultFilter, + attributes: AttributeContainer([.font: UIFont.pretendardBold(13)])) + $0.configuration?.cornerStyle = .capsule + $0.configuration?.imagePadding = 5 + $0.configuration?.contentInsets = .zero + $0.addAction(UIAction { _ in + print("tapped") + }, for: .touchUpInside) + } + + lineView.do { + $0.backgroundColor = .gbbGray200 + } + [topView, bottomView].forEach { + $0.backgroundColor = .gbbGray200 + } + } + + private func setLayout() { + addSubviews(topView, filterButton, lineView, filterCollectionView, bottomView) + + topView.snp.makeConstraints { + $0.top.directionalHorizontalEdges.equalToSuperview() + $0.height.equalTo(1) + } + + filterButton.snp.makeConstraints { + $0.leading.equalToSuperview().offset(24) + $0.centerY.equalToSuperview() + $0.size.equalTo(CGSize(width: 87, height: 36)) + } + + lineView.snp.makeConstraints { + $0.width.equalTo(1) + $0.leading.equalTo(filterButton.snp.trailing).offset(12) + $0.directionalVerticalEdges.equalToSuperview().inset(15) + } + + filterCollectionView.snp.makeConstraints { + $0.leading.equalTo(lineView.snp.trailing) + $0.centerY.trailing.equalToSuperview() + $0.height.equalTo(42) + } + + bottomView.snp.makeConstraints { + $0.directionalHorizontalEdges.bottom.equalToSuperview() + $0.height.equalTo(1) + } + } + + func layout() -> UICollectionViewFlowLayout { + let layout = UICollectionViewFlowLayout() + layout.minimumLineSpacing = 10 + layout.scrollDirection = .horizontal + layout.sectionInset = .init(top: 0, left: 13, bottom: 0, right: 13) + return layout + } + + private func setRegistration() { + filterCollectionView.register(cell: BakeryFilterCollectionViewCell.self) + } + + private func setDataSource() { + dataSource = DataSource(collectionView: filterCollectionView, cellProvider: { collectionView, indexPath, item in + let cell: BakeryFilterCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.bind(item: item, index: indexPath.item) + return cell + }) + } + + private func setReloadData() { + var snapshot = NSDiffableDataSourceSnapshot() + defer { dataSource?.apply(snapshot, animatingDifferences: false)} + snapshot.appendSections([.main]) + snapshot.appendItems(filterlist) + } +} + +extension BakeryFilterView: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + if self.filterlist[indexPath.item].status == .off { + self.filterlist[indexPath.item].status = .on + } else { + self.filterlist[indexPath.item].status = .off + } + self.filterlist[indexPath.item] = self.filterlist[indexPath.item].isSelected() + + var snapshot = NSDiffableDataSourceSnapshot() + snapshot.appendSections([.main]) + snapshot.appendItems(filterlist) + dataSource?.apply(snapshot, animatingDifferences: true) + } +} + +extension BakeryFilterView: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let cellWidth: CGFloat + let cell: BakeryFilterCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.filterTitle.text = filterlist[indexPath.row].filter.title + cell.getSize() + cellWidth = cell.filterTitle.frame.width + 48 + + return CGSize(width: cellWidth, height: 36) + } +} diff --git a/GEON-PPANG-iOS/Presentation/BakeryList/Views/BakeryListTopView.swift b/GEON-PPANG-iOS/Presentation/BakeryList/Views/BakeryListTopView.swift new file mode 100644 index 00000000..3e6a98bc --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/BakeryList/Views/BakeryListTopView.swift @@ -0,0 +1,80 @@ +// +// BakeryListTopView.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/11. +// + +import UIKit + +import SnapKit +import Then + +final class BakeryListTopView: UIView { + + // MARK: - UI Property + + private let hStackView = UIStackView() + private let bakeryTitle = UILabel() + private let bakeryIcon = UIImageView() + private let searchButton = UIButton() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setUI() { + hStackView.do { + $0.axis = .horizontal + $0.spacing = 8 + $0.sizeToFit() + } + + bakeryTitle.do { + $0.basic(text: I18N.BakeryList.bakeryTitle, + font: .title1!, + color: .gbbGray700!) + } + + bakeryIcon.do { + $0.image = .listIcon + $0.contentMode = .scaleAspectFit + } + + searchButton.do { + $0.setImage(.searchIcon600, for: .normal) + } + } + + private func setLayout() { + addSubviews(hStackView, searchButton) + hStackView.addArrangedSubviews(bakeryTitle, bakeryIcon) + + hStackView.snp.makeConstraints { + $0.leading.equalToSuperview().offset(24) + $0.bottom.equalToSuperview().offset(-15) + $0.top.equalToSuperview().offset(44) + } + + bakeryIcon.snp.makeConstraints { + $0.size.equalTo(24) + } + + searchButton.snp.makeConstraints { + $0.top.equalTo(hStackView.snp.top) + $0.trailing.equalToSuperview().offset(-23) + $0.bottom.equalToSuperview().offset(-15) + } + } +} diff --git a/GEON-PPANG-iOS/Presentation/Common/Button/CommonButton.swift b/GEON-PPANG-iOS/Presentation/Common/Button/CommonButton.swift index 59e0c24f..b5663989 100644 --- a/GEON-PPANG-iOS/Presentation/Common/Button/CommonButton.swift +++ b/GEON-PPANG-iOS/Presentation/Common/Button/CommonButton.swift @@ -40,7 +40,7 @@ final class CommonButton: UIButton { private func setUI() { self.do { - $0.makeCornerRound(radius: 11) + $0.makeCornerRound(radius: 12) $0.titleLabel?.font = .headLine } } @@ -52,17 +52,13 @@ final class CommonButton: UIButton { func getButtonUI(_ color: UIColor, _ border: UIColor? = .clear) { self.backgroundColor = color switch color { - case .gbbMain3!, .gbbGray700!: setTitleColor(.gbbGray100, for: .normal) - case .gbbMain2!: setTitleColor(.white, for: .normal) + case .gbbMain2!, .gbbGray700!: setTitleColor(.gbbGray100, for: .normal) default: setTitleColor(.gbbGray400, for: .normal) } - if let border = border { - makeBorder(width: 1, color: border) - if border != .clear { - setTitleColor(border, for: .normal) - } + if border != .clear { + makeBorder(width: 1, color: border!) } } diff --git a/GEON-PPANG-iOS/Presentation/Common/Cell/DescriptionCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/Common/Cell/DescriptionCollectionViewCell.swift index ac69121b..793e7636 100644 --- a/GEON-PPANG-iOS/Presentation/Common/Cell/DescriptionCollectionViewCell.swift +++ b/GEON-PPANG-iOS/Presentation/Common/Cell/DescriptionCollectionViewCell.swift @@ -69,7 +69,7 @@ final class DescriptionCollectionViewCell: UICollectionViewCell { private func setLayout() { addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { - $0.horizontalEdges.equalToSuperview().inset(8) + $0.horizontalEdges.equalToSuperview().inset(6) $0.verticalEdges.equalToSuperview().inset(4) } } diff --git a/GEON-PPANG-iOS/Presentation/Common/DrawDashLineView.swift b/GEON-PPANG-iOS/Presentation/Common/DrawDashLineView.swift new file mode 100644 index 00000000..3bbc6b09 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Common/DrawDashLineView.swift @@ -0,0 +1,33 @@ +// +// DrawDashLineView.swift +// GEON-PPANG-iOS +// +// Created by kyun on 2023/07/11. +// + +import UIKit + +final class DrawDashLineView: UIView { // 코너가 들어간 점선 그리기 + + private let borderLayer = CAShapeLayer() + + override init(frame: CGRect) { + super.init(frame: frame) + + borderLayer.strokeColor = UIColor.gbbMain3?.cgColor + borderLayer.lineDashPattern = [2, 2] + borderLayer.backgroundColor = UIColor.clear.cgColor + borderLayer.fillColor = UIColor.clear.cgColor + + layer.addSublayer(borderLayer) + } + + override func draw(_ rect: CGRect) { + + borderLayer.path = UIBezierPath(roundedRect: rect, cornerRadius: 15).cgPath + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/GEON-PPANG-iOS/Presentation/Common/CustomNavigationBar.swift b/GEON-PPANG-iOS/Presentation/Common/NavigationBar/CustomNavigationBar.swift similarity index 89% rename from GEON-PPANG-iOS/Presentation/Common/CustomNavigationBar.swift rename to GEON-PPANG-iOS/Presentation/Common/NavigationBar/CustomNavigationBar.swift index ea0d5845..ab95bb54 100644 --- a/GEON-PPANG-iOS/Presentation/Common/CustomNavigationBar.swift +++ b/GEON-PPANG-iOS/Presentation/Common/NavigationBar/CustomNavigationBar.swift @@ -44,7 +44,7 @@ final class CustomNavigationBar: UIView { private func setLayout() { self.snp.makeConstraints { - $0.height.equalTo(118) + $0.height.equalTo(CGFloat().heightConsideringNotch(118)) } addSubview(backButton) @@ -94,7 +94,7 @@ final class CustomNavigationBar: UIView { rightCountLabel.do { $0.text = "\(currentCount)/\(maxCount)" $0.font = .pretendardMedium(15) - $0.textColor = .gbbGray700 + $0.textColor = .gbbGray300 } } @@ -117,4 +117,13 @@ final class CustomNavigationBar: UIView { } } + func configureBottomLine() { + let bottomLine = LineView() + addSubview(bottomLine) + bottomLine.snp.makeConstraints { + $0.bottom.horizontalEdges.equalToSuperview() + $0.height.equalTo(1) + } + } + } diff --git a/GEON-PPANG-iOS/Presentation/Common/StackView/MarkStackView.swift b/GEON-PPANG-iOS/Presentation/Common/StackView/MarkStackView.swift index 223393c7..387325c1 100644 --- a/GEON-PPANG-iOS/Presentation/Common/StackView/MarkStackView.swift +++ b/GEON-PPANG-iOS/Presentation/Common/StackView/MarkStackView.swift @@ -11,7 +11,7 @@ import SnapKit import Then final class MarkStackView: UIStackView { - + // MARK: - UI Property private lazy var hccpMarkIconView = UIImageView() @@ -36,19 +36,19 @@ final class MarkStackView: UIStackView { private func setUI() { self.do { $0.addArrangedSubviews(hccpMarkIconView, veganIconView, gmoIconView) - $0.spacing = -9 + $0.spacing = -8 $0.axis = .horizontal } + hccpMarkIconView.do { - $0.image = .bigHACCPMark - $0.contentMode = .scaleAspectFit + $0.contentMode = .topLeft } + veganIconView.do { - $0.image = .bigVeganMark $0.contentMode = .topLeft } + gmoIconView.do { - $0.image = .bigGMOMark $0.contentMode = .topLeft } } @@ -56,20 +56,32 @@ final class MarkStackView: UIStackView { private func setLayout() { [hccpMarkIconView, veganIconView, gmoIconView].forEach { $0.snp.makeConstraints { - $0.size.equalTo(28) + $0.size.equalTo(24) } } } + func getIconImage(_ haccp: UIImage, _ vegan: UIImage, _ gmo: UIImage) { + hccpMarkIconView.image = haccp + veganIconView.image = vegan + gmoIconView.image = gmo + } + func getMarkStatus(_ isHACCP: Bool, _ isVegan: Bool, _ isNONGMO: Bool) { if !isHACCP { hccpMarkIconView.isHidden = true + } else { + hccpMarkIconView.isHidden = false } if !isVegan { veganIconView.isHidden = true + } else { + veganIconView.isHidden = false } if !isNONGMO { gmoIconView.isHidden = true + } else { + gmoIconView.isHidden = false } } } diff --git a/GEON-PPANG-iOS/Presentation/Common/StackView/RegionStackView.swift b/GEON-PPANG-iOS/Presentation/Common/StackView/RegionStackView.swift index 0adea678..fdae951e 100644 --- a/GEON-PPANG-iOS/Presentation/Common/StackView/RegionStackView.swift +++ b/GEON-PPANG-iOS/Presentation/Common/StackView/RegionStackView.swift @@ -15,7 +15,7 @@ final class RegionStackView: UIStackView { // MARK: - UI Property private lazy var regionFirstTag = PaddingLabel() - private lazy var regionSeconodTag = PaddingLabel() + private lazy var regionSecondTag = PaddingLabel() // MARK: - Life Cycle @@ -33,33 +33,33 @@ final class RegionStackView: UIStackView { private func setUI() { self.do { - $0.addArrangedSubviews(regionFirstTag, regionSeconodTag) + $0.addArrangedSubviews(regionFirstTag, regionSecondTag) $0.axis = .horizontal $0.spacing = 5 $0.distribution = .fillProportionally } - [regionFirstTag, regionSeconodTag] .forEach { + [regionFirstTag, regionSecondTag].forEach { $0.makeCornerRound(radius: 14.5) - $0.font = .pretendardMedium(13) + $0.font = .captionM1 $0.backgroundColor = .gbbMain3 $0.textColor = .gbbGray100 } } - + func getRegionName(_ first: String, _ second: String) { regionFirstTag.text = first - regionSeconodTag.text = second + regionSecondTag.text = second } func getBackgroundColor(_ color: UIColor) { - [regionFirstTag, regionSeconodTag] .forEach { + [regionFirstTag, regionSecondTag] .forEach { $0.backgroundColor = color } } func removeSecondRegion() { - self.removeArrangedSubview(regionSeconodTag) - regionSeconodTag.removeFromSuperview() + self.removeArrangedSubview(regionSecondTag) + regionSecondTag.removeFromSuperview() } } diff --git a/GEON-PPANG-iOS/Presentation/Common/View/BottomView.swift b/GEON-PPANG-iOS/Presentation/Common/View/BottomView.swift new file mode 100644 index 00000000..d42391f0 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Common/View/BottomView.swift @@ -0,0 +1,57 @@ +// +// BottomView.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/11. +// + +import UIKit + +import SnapKit +import Then + +final class BottomView: UIView { + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: frame) + + setLayout() + setShadow() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + self.snp.makeConstraints { + $0.height.equalTo(128) + } + } + + private func setShadow() { + self.layer.applyShadow(color: .init(red: 0, green: 0, blue: 0, alpha: 0.1), + alpha: 1, + x: 0, + y: 1, + blur: 10) + } + + // MARK: - Custom Method + + func applyAdditionalSubview(_ view: UIView, withTrailingOffset offset: CGFloat = 21) { + addSubview(view) + view.snp.makeConstraints { + $0.leading.equalToSuperview().inset(24) + $0.trailing.equalToSuperview().inset(24) + $0.top.equalToSuperview().inset(offset) + $0.height.equalTo(56) + } + } + +} diff --git a/GEON-PPANG-iOS/Presentation/Common/LineView.swift b/GEON-PPANG-iOS/Presentation/Common/View/LineView.swift similarity index 98% rename from GEON-PPANG-iOS/Presentation/Common/LineView.swift rename to GEON-PPANG-iOS/Presentation/Common/View/LineView.swift index 6edbcfd4..6c1d344f 100644 --- a/GEON-PPANG-iOS/Presentation/Common/LineView.swift +++ b/GEON-PPANG-iOS/Presentation/Common/View/LineView.swift @@ -31,6 +31,5 @@ final class LineView: UIView { $0.backgroundColor = .gbbGray200 } } - - + } diff --git a/GEON-PPANG-iOS/Presentation/Filter/Model/FilterDTO.swift b/GEON-PPANG-iOS/Presentation/Filter/Model/FilterDTO.swift new file mode 100644 index 00000000..86fe98b1 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Filter/Model/FilterDTO.swift @@ -0,0 +1,56 @@ +// +// FilterDTO.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/13. +// + +import Foundation + +struct FilterRequestDTO { + var mainPurpose: String + var breadType: BreadRequestType + var nutrientType: NutrientType + + static var sharedData = FilterRequestDTO() + + private init() { + self.mainPurpose = "" + self.breadType = .init(isGlutenFree: false, + isVegan: false, + isNutFree: false, + isSugarFree: false) + self.nutrientType = .init(isNutrientOpen: false, + isIngredientOpen: false, + isNotOpen: false) + } +} + +struct BreadRequestType { + var isGlutenFree: Bool + var isVegan: Bool + var isNutFree: Bool + var isSugarFree: Bool + + func isNoneSelected() -> Bool { + if isGlutenFree == false && isVegan == false && isNutFree == false && isSugarFree == false { + return true + } else { + return false + } + } +} + +struct NutrientType { + var isNutrientOpen: Bool + var isIngredientOpen: Bool + var isNotOpen: Bool + + func isNoneSelected() -> Bool { + if isNutrientOpen == false && isIngredientOpen == false && isNotOpen == false { + return true + } else { + return false + } + } +} diff --git a/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterBreadTypeViewController.swift b/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterBreadTypeViewController.swift new file mode 100644 index 00000000..ab1aac5f --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterBreadTypeViewController.swift @@ -0,0 +1,226 @@ +// +// FilterBreadTypeViewController.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class FilterBreadTypeViewController: BaseViewController { + + // MARK: - Property + + private var maxSteps: Int = 0 + private var filterType: FilterType = .breadType + + private let filterTypes: [String] = FilterBreadType.allCases.map { $0.rawValue } + private let filterDescriptions: [String] = FilterBreadType.allCases.map { $0.description } + + // MARK: - UI Property + + private let navigationBar = CustomNavigationBar() + private let filterTitleLabel = UILabel() + private let filterSubtitleLabel = UILabel() + private let filterCollectionViewFlowLayout = UICollectionViewFlowLayout() + private lazy var filterCollectionView = UICollectionView(frame: .zero, collectionViewLayout: filterCollectionViewFlowLayout) + private let nextButton = CommonButton() + + // MARK: - Life Cycle + + init(maxSteps: Int) { + super.init(nibName: nil, bundle: nil) + + setMaxSteps(to: maxSteps) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + setNextButtonAction() + } + + // MARK: - Setting + + private func setMaxSteps(to steps: Int) { + self.maxSteps = steps + } + + override func setLayout() { + view.addSubview(navigationBar) + navigationBar.snp.makeConstraints { + $0.top.horizontalEdges.equalToSuperview() + } + + view.addSubview(filterTitleLabel) + filterTitleLabel.snp.makeConstraints { + $0.top.equalTo(navigationBar.snp.bottom) + $0.leading.equalToSuperview().inset(24) + } + + view.addSubview(filterSubtitleLabel) + filterSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(filterTitleLabel.snp.bottom).offset(12) + $0.leading.equalToSuperview().inset(24) + } + + view.addSubview(nextButton) + nextButton.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalToSuperview().inset(CGFloat().heightConsideringBottomSafeArea(55)) + $0.height.equalTo(56) + } + + view.addSubview(filterCollectionView) + filterCollectionView.snp.makeConstraints { + $0.top.equalTo(filterSubtitleLabel.snp.bottom).offset(40) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalTo(nextButton.snp.top).offset(-10) + } + } + + override func setUI() { + navigationBar.do { + $0.addBackButtonAction(popFilterViewController()) + $0.configureRightCount(maxSteps - 1, by: maxSteps) + } + + filterTitleLabel.do { + $0.text = I18N.Filter.breadTypeTitle + $0.font = .title1 + $0.textColor = .gbbGray700 + $0.numberOfLines = 1 + } + + filterSubtitleLabel.do { + $0.text = I18N.Filter.breadTypeSubtitle + $0.font = .subHead + $0.textColor = .gbbGray400 + } + + filterCollectionViewFlowLayout.do { + $0.scrollDirection = .vertical + $0.minimumLineSpacing = CGFloat().convertByWidthRatio(21) + $0.minimumInteritemSpacing = CGFloat().convertByHeightRatio(20) + } + + filterCollectionView.do { + $0.register(cell: FilterCollectionViewCell.self) + $0.backgroundColor = .clear + $0.isScrollEnabled = false + $0.allowsMultipleSelection = true + } + + nextButton.do { + $0.getButtonTitle(.next) + $0.getButtonUI(.gbbGray200!) + $0.isUserInteractionEnabled = false + } + } + + override func setDelegate() { + filterCollectionView.delegate = self + filterCollectionView.dataSource = self + } + + private func setNextButtonAction() { + let action = UIAction { [weak self] _ in + Utils.push(self?.navigationController, FilterIngredientViewController(maxSteps: self?.maxSteps ?? 0)) + } + nextButton.addAction(action, for: .touchUpInside) + } + + // MARK: - Action Helper + + private func popFilterViewController() -> UIAction { + let action = UIAction { [weak self] _ in + FilterRequestDTO.sharedData.breadType = .init( + isGlutenFree: false, + isVegan: false, + isNutFree: false, + isSugarFree: false + ) + self?.navigationController?.popViewController(animated: true) + } + return action + } + + // MARK: - Custom Method + + private func checkNextButtonStatus() { + if FilterRequestDTO.sharedData.breadType.isNoneSelected() { + nextButton.isUserInteractionEnabled = false + UIView.animate(withDuration: 0.2) { + self.nextButton.getButtonUI(.gbbGray200!) + } + } else { + nextButton.isUserInteractionEnabled = true + UIView.animate(withDuration: 0.2) { + self.nextButton.getButtonUI(.gbbMain2!) + } + } + } + +} + +// MARK: - UICollectionViewDelegate extension + +extension FilterBreadTypeViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + switch indexPath.item { + case 0: FilterRequestDTO.sharedData.breadType.isGlutenFree = true + case 1: FilterRequestDTO.sharedData.breadType.isVegan = true + case 2: FilterRequestDTO.sharedData.breadType.isNutFree = true + case 3: FilterRequestDTO.sharedData.breadType.isSugarFree = true + default: return + } + checkNextButtonStatus() + } + + func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + switch indexPath.item { + case 0: FilterRequestDTO.sharedData.breadType.isGlutenFree = false + case 1: FilterRequestDTO.sharedData.breadType.isVegan = false + case 2: FilterRequestDTO.sharedData.breadType.isNutFree = false + case 3: FilterRequestDTO.sharedData.breadType.isSugarFree = false + default: return + } + checkNextButtonStatus() + } + +} + +// MARK: - UICollectionViewDataSource extension + +extension FilterBreadTypeViewController: UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return filterTypes.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: FilterCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.filterType = self.filterType + cell.typeLabelText = filterTypes[indexPath.item] + cell.descriptionLabelText = filterDescriptions[indexPath.item] + return cell + } + +} + +extension FilterBreadTypeViewController: UICollectionViewDelegateFlowLayout { + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return filterType.cellSize + } + +} diff --git a/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterIngredientViewController.swift b/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterIngredientViewController.swift new file mode 100644 index 00000000..fe6bde21 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterIngredientViewController.swift @@ -0,0 +1,220 @@ +// +// FilterIngredientViewController.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class FilterIngredientViewController: BaseViewController { + + // MARK: - Property + + private var maxSteps: Int = 0 + private let filterType: FilterType = .ingredient + + private let filterTypes: [String] = FilterIngredientType.allCases.map { $0.rawValue } + + // MARK: - UI Property + + private let navigationBar = CustomNavigationBar() + private let filterTitleLabel = UILabel() + private let filterSubtitleLabel = UILabel() + private let filterCollectionViewFlowLayout = UICollectionViewFlowLayout() + private lazy var filterCollectionView = UICollectionView(frame: .zero, collectionViewLayout: filterCollectionViewFlowLayout) + private let nextButton = CommonButton() + + // MARK: - Life Cycle + + init(maxSteps: Int) { + super.init(nibName: nil, bundle: nil) + + setMaxSteps(to: maxSteps) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + setNextButtonAction() + } + + // MARK: - Setting + + private func setMaxSteps(to steps: Int) { + self.maxSteps = steps + } + + override func setLayout() { + view.addSubview(navigationBar) + navigationBar.snp.makeConstraints { + $0.top.horizontalEdges.equalToSuperview() + } + + view.addSubview(filterTitleLabel) + filterTitleLabel.snp.makeConstraints { + $0.top.equalTo(navigationBar.snp.bottom) + $0.leading.equalToSuperview().inset(24) + } + + view.addSubview(filterSubtitleLabel) + filterSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(filterTitleLabel.snp.bottom).offset(12) + $0.leading.equalToSuperview().inset(24) + } + + view.addSubview(nextButton) + nextButton.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalToSuperview().inset(CGFloat().heightConsideringBottomSafeArea(55)) + $0.height.equalTo(56) + } + + view.addSubview(filterCollectionView) + filterCollectionView.snp.makeConstraints { + $0.top.equalTo(filterSubtitleLabel.snp.bottom).offset(40) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalTo(nextButton.snp.top).offset(-10) + } + } + + override func setUI() { + navigationBar.do { + $0.addBackButtonAction(popFilterViewController()) + $0.configureRightCount(maxSteps, by: maxSteps) + } + + filterTitleLabel.do { + $0.text = I18N.Filter.ingredientTitle + $0.font = .title1 + $0.textColor = .gbbGray700 + $0.numberOfLines = 2 + } + + filterSubtitleLabel.do { + $0.text = I18N.Filter.ingredientSubtitle + $0.font = .subHead + $0.textColor = .gbbGray400 + } + + filterCollectionViewFlowLayout.do { + $0.scrollDirection = .vertical + $0.minimumLineSpacing = 20 + } + + filterCollectionView.do { + $0.register(cell: FilterCollectionViewCell.self) + $0.backgroundColor = .clear + $0.isScrollEnabled = false + $0.allowsMultipleSelection = true + } + + nextButton.do { + $0.getButtonTitle(.next) + $0.getButtonUI(.gbbGray200!) + $0.isUserInteractionEnabled = false + } + } + + override func setDelegate() { + filterCollectionView.delegate = self + filterCollectionView.dataSource = self + } + + private func setNextButtonAction() { + let action = UIAction { [weak self] _ in + if self?.maxSteps != 6 { + self?.navigationController?.popToRootViewController(animated: true) + } else { + + } + } + nextButton.addAction(action, for: .touchUpInside) + } + + // MARK: - Action Helper + + private func popFilterViewController() -> UIAction { + let action = UIAction { [weak self] _ in + FilterRequestDTO.sharedData.nutrientType = .init( + isNutrientOpen: false, + isIngredientOpen: false, + isNotOpen: false + ) + self?.navigationController?.popViewController(animated: true) + } + return action + } + + // MARK: - Custom Method + + private func checkNextButtonStatus() { + if FilterRequestDTO.sharedData.nutrientType.isNoneSelected() { + nextButton.isUserInteractionEnabled = false + UIView.animate(withDuration: 0.2) { + self.nextButton.getButtonUI(.gbbGray200!) + } + } else { + nextButton.isUserInteractionEnabled = true + UIView.animate(withDuration: 0.2) { + self.nextButton.getButtonUI(.gbbMain2!) + } + } + } + +} + +// MARK: - UICollectionViewDelegate extension + +extension FilterIngredientViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + switch indexPath.item { + case 0: FilterRequestDTO.sharedData.nutrientType.isNutrientOpen = true + case 1: FilterRequestDTO.sharedData.nutrientType.isIngredientOpen = true + case 2: FilterRequestDTO.sharedData.nutrientType.isNotOpen = true + default: return + } + checkNextButtonStatus() + } + + func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + switch indexPath.item { + case 0: FilterRequestDTO.sharedData.nutrientType.isNutrientOpen = false + case 1: FilterRequestDTO.sharedData.nutrientType.isIngredientOpen = false + case 2: FilterRequestDTO.sharedData.nutrientType.isNotOpen = false + default: return + } + checkNextButtonStatus() + } + +} + +// MARK: - UICollectionViewDataSource extension + +extension FilterIngredientViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return filterTypes.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: FilterCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.filterType = self.filterType + cell.typeLabelText = filterTypes[indexPath.item] + return cell + } +} + +extension FilterIngredientViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return filterType.cellSize + } +} diff --git a/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterPurposeViewController.swift b/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterPurposeViewController.swift new file mode 100644 index 00000000..d2f31ba8 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Filter/ViewControllers/FilterPurposeViewController.swift @@ -0,0 +1,189 @@ +// +// FilterPurposeViewController.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class FilterPurposeViewController: BaseViewController { + + // MARK: - Property + + private var maxSteps: Int = 0 + private var userName: String = "Id" + + private var filterType: FilterType = .purpose + + private let filterTypes: [String] = FilterPurposeType.allCases.map { $0.rawValue } + private let filterDescriptions: [String] = FilterPurposeType.allCases.map { $0.description } + private let filterDatas: [String] = FilterPurposeType.allCases.map { $0.data } + + // MARK: - UI Property + + private let navigationBar = CustomNavigationBar() + private let filterTitleLabel = UILabel() + private let filterCollectionViewFlowLayout = UICollectionViewFlowLayout() + private lazy var filterCollectionView = UICollectionView(frame: .zero, collectionViewLayout: filterCollectionViewFlowLayout) + private let nextButton = CommonButton() + + // MARK: - Life Cycle + + init(maxSteps: Int, username: String) { + super.init(nibName: nil, bundle: nil) + + setMaxSteps(to: maxSteps) + setUsername(to: username) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + setNextButtonAction() + } + + // MARK: - Setting + + private func setMaxSteps(to steps: Int) { + self.maxSteps = steps + } + + private func setUsername(to name: String) { + self.userName = name + } + + override func setLayout() { + view.addSubview(navigationBar) + navigationBar.snp.makeConstraints { + $0.top.horizontalEdges.equalToSuperview() + } + + view.addSubview(filterTitleLabel) + filterTitleLabel.snp.makeConstraints { + $0.top.equalTo(navigationBar.snp.bottom) + $0.leading.equalToSuperview().inset(24) + } + + view.addSubview(nextButton) + nextButton.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalToSuperview().inset(CGFloat().heightConsideringBottomSafeArea(55)) + $0.height.equalTo(56) + } + + view.addSubview(filterCollectionView) + filterCollectionView.snp.makeConstraints { + $0.top.equalTo(filterTitleLabel.snp.bottom).offset(40) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.bottom.equalTo(nextButton.snp.top).offset(-10) + } + } + + override func setUI() { + navigationBar.do { + $0.addBackButtonAction(popFilterViewController()) + $0.configureRightCount(maxSteps - 2, by: maxSteps) + } + + filterTitleLabel.do { + $0.text = I18N.Filter.purposeTitle.insertString(userName, at: 5) + $0.font = .title1 + $0.textColor = .gbbGray700 + $0.numberOfLines = 2 + } + + filterCollectionViewFlowLayout.do { + $0.scrollDirection = .vertical + $0.minimumLineSpacing = 20 + } + + filterCollectionView.do { + $0.register(cell: FilterCollectionViewCell.self) + $0.backgroundColor = .clear + $0.isScrollEnabled = false + } + + nextButton.do { + $0.getButtonTitle(.next) + $0.getButtonUI(.gbbGray200!) + $0.isUserInteractionEnabled = false + } + } + + override func setDelegate() { + filterCollectionView.delegate = self + filterCollectionView.dataSource = self + } + + private func setNextButtonAction() { + let action = UIAction { [weak self] _ in + Utils.push(self?.navigationController, FilterBreadTypeViewController(maxSteps: self?.maxSteps ?? 0)) + dump(FilterRequestDTO.sharedData) + } + nextButton.addAction(action, for: .touchUpInside) + } + + // MARK: - Action Helper + + private func popFilterViewController() -> UIAction { + let action = UIAction { [weak self] _ in + FilterRequestDTO.sharedData.mainPurpose = "" + self?.navigationController?.popViewController(animated: true) + } + return action + } + + // MARK: - Custom Method + + private func enableNextButton() { + UIView.animate(withDuration: 0.2) { + self.nextButton.do { + $0.getButtonUI(.gbbMain2!) + $0.isUserInteractionEnabled = true + } + } + } + +} + +// MARK: - UICollectionViewDelegate extension + +extension FilterPurposeViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + FilterRequestDTO.sharedData.mainPurpose = filterDatas[indexPath.item] + enableNextButton() + } + +} + +// MARK: - UICollectionViewDataSource extension + +extension FilterPurposeViewController: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return filterTypes.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: FilterCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.filterType = .purpose + cell.typeLabelText = filterTypes[indexPath.item] + cell.descriptionLabelText = filterDescriptions[indexPath.item] + return cell + } +} + +extension FilterPurposeViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return filterType.cellSize + } +} diff --git a/GEON-PPANG-iOS/Presentation/Filter/Views/FilterCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/Filter/Views/FilterCollectionViewCell.swift new file mode 100644 index 00000000..9bd82e1b --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Filter/Views/FilterCollectionViewCell.swift @@ -0,0 +1,113 @@ +// +// FilterCollectionViewCell.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class FilterCollectionViewCell: UICollectionViewCell { + + // MARK: - Property + + override var isSelected: Bool { + didSet { + toggleSelection() + } + } + + var filterType: FilterType = .purpose { + didSet { + configureLayout() + configureUI() + } + } + + var typeLabelText: String = "" { + willSet { + configureLabelText(type: newValue) + } + } + + var descriptionLabelText: String = "" { + willSet { + configureLabelText(description: newValue) + } + } + + // MARK: - UI Property + + private let typeLabel = UILabel() + private let descriptionLabel = UILabel() + private lazy var labelStackView = UIStackView(arrangedSubviews: [typeLabel, descriptionLabel]) + + // MARK: - Custom Method + + private func configureLayout() { + contentView.addSubview(labelStackView) + labelStackView.snp.makeConstraints { + $0.center.equalToSuperview() + } + } + + private func configureUI() { + self.do { + $0.backgroundColor = .gbbBackground2 + $0.makeBorder(width: 1, color: .gbbGray300!) + $0.makeCornerRound(radius: 10) + } + + typeLabel.do { + $0.font = .title2 + $0.textColor = .gbbGray300 + } + + descriptionLabel.do { + $0.font = .bodyM1 + $0.textColor = .gbbGray300 + } + + labelStackView.do { + $0.axis = .vertical + $0.spacing = filterType.labelSpacing + $0.alignment = .center + } + } + + private func configureLabelText(type typeText: String) { + typeLabel.do { + $0.text = typeText + } + } + + private func configureLabelText(description descriptionText: String) { + descriptionLabel.do { + $0.text = descriptionText + } + } + + private func toggleSelection() { + UIView.animate(withDuration: 0.2) { + self.do { + $0.makeBorder(width: self.isSelected ? 2 : 1, color: self.isSelected ? .gbbMain1! : .gbbGray300!) + } + } + + UIView.transition(with: typeLabel, duration: 0.2, options: .transitionCrossDissolve) { + self.typeLabel.do { + $0.textColor = self.isSelected ? .gbbMain1 : .gbbGray300 + } + } + + UIView.transition(with: descriptionLabel, duration: 0.2, options: .transitionCrossDissolve) { + self.descriptionLabel.do { + $0.textColor = self.isSelected ? .gbbMain1 : .gbbGray300 + } + } + } + +} diff --git a/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestBakeryResponseDTO.swift b/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestBakeryResponseDTO.swift index d66d0fd3..a64bd806 100644 --- a/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestBakeryResponseDTO.swift +++ b/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestBakeryResponseDTO.swift @@ -10,9 +10,11 @@ struct HomeBestBakeryResponseDTO: Hashable { let bakeryID: Int let bakeryName: String - let isHACCP, isVegan: Bool + let isHACCP: Bool + let isVegan: Bool let isNONGMO: Bool - let firstNearStation, secondNearStation: String + let firstNearStation: String + let secondNearStation: String? let isBooked: Bool let bookmarkCount: Int let bakeryPicture: String diff --git a/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestReviewResponseDTO.swift b/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestReviewResponseDTO.swift index 2c8ea709..7ca41b96 100644 --- a/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestReviewResponseDTO.swift +++ b/GEON-PPANG-iOS/Presentation/Home/Model/HomeBestReviewResponseDTO.swift @@ -13,14 +13,18 @@ struct HomeBestReviewResponseDTO: Hashable { let bakeryID: Int let bakeryName: String - let isHACCP, isVegan: Bool - let isNONGMO: Bool? - let firstNearStation, secondNearStation: String + let isHACCP: Bool + let isVegan: Bool + let isNONGMO: Bool + let firstNearStation: String + let secondNearStation: String? let isBooked: Bool let bookmarkCount: Int let bakeryPicture: String let reviewCount: Int - let reviewText, firstMaxRecommendKeyword, secondMaxRecommendKeyword: String + let reviewText: String + let firstMaxRecommendKeyword: String + let secondMaxRecommendKeyword: String? } extension HomeBestReviewResponseDTO { diff --git a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBakeryCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBakeryCollectionViewCell.swift index 2837e565..e265c57d 100644 --- a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBakeryCollectionViewCell.swift +++ b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBakeryCollectionViewCell.swift @@ -25,12 +25,14 @@ final class HomeBakeryCollectionViewCell: UICollectionViewCell { private let bakeryReview = UILabel() private let regionStackView = RegionStackView() private lazy var bookMarkButton = BookmarkButton(configuration: .plain()) - + // MARK: - Life Cycle override func prepareForReuse() { super.prepareForReuse() markStackView.getMarkStatus(false, false, false) + markStackView.getIconImage(.bigHACCPMark, .bigVeganMark, .bigGMOMark) + bookMarkButton.getCount(0) } override init(frame: CGRect) { super.init(frame: .zero) @@ -61,12 +63,22 @@ final class HomeBakeryCollectionViewCell: UICollectionViewCell { $0.contentMode = .scaleAspectFill $0.backgroundColor = .gbbPoint1 } + markStackView.do { + $0.getIconImage(.bigHACCPMark, .bigVeganMark, .bigGMOMark) + } + + bakeryTitle.do { + $0.basic(font: .bodyB2!, color: .gbbGray700!) + } + + bakeryReview.do { + $0.basic(font: .pretendardBold(13), color: .gbbGray700!) + } [bakeryTitle, bakeryReview].forEach { $0.do { $0.numberOfLines = 1 $0.textAlignment = .left - $0.basic(font: .pretendardBold(14), color: .gbbGray700!) } } } @@ -74,11 +86,7 @@ final class HomeBakeryCollectionViewCell: UICollectionViewCell { private func setLayout() { contentView.addSubviews(bakeryImage, bookMarkButton, bakeryTitle, bakeryReview, regionStackView) bakeryImage.addSubview(markStackView) - - let availableHeight = contentView.bounds.height - let buttonHeight: CGFloat = 60 - let buttonTopOffset = (availableHeight - buttonHeight) / 2 - + bakeryImage.snp.makeConstraints { $0.top.directionalHorizontalEdges.equalToSuperview() $0.height.equalTo(118) @@ -91,26 +99,25 @@ final class HomeBakeryCollectionViewCell: UICollectionViewCell { bookMarkButton.snp.makeConstraints { $0.trailing.equalToSuperview().inset(16) - $0.top.equalTo(bakeryReview.snp.bottom).offset(buttonTopOffset) + $0.top.equalTo(bakeryImage.snp.bottom).offset(29) $0.size.equalTo(CGSize(width: 34, height: 60)) } bakeryTitle.snp.makeConstraints { - $0.top.equalTo(bakeryImage.snp.bottom).offset(18) - $0.leading.equalToSuperview().offset(13) + $0.top.equalTo(bakeryImage.snp.bottom).offset(15) + $0.leading.equalToSuperview().offset(16) $0.trailing.equalTo(bookMarkButton.snp.leading).offset(-14) } bakeryReview.snp.makeConstraints { - $0.top.equalTo(bakeryTitle.snp.bottom).offset(10) + $0.top.equalTo(bakeryTitle.snp.bottom).offset(6) $0.leading.equalTo(bakeryTitle.snp.leading) - $0.trailing.equalTo(bookMarkButton.snp.leading).offset(-20) } regionStackView.snp.makeConstraints { - $0.top.equalTo(bakeryReview.snp.bottom).offset(10) + $0.top.equalTo(bakeryReview.snp.bottom).offset(14) $0.leading.equalTo(bakeryTitle.snp.leading) - $0.bottom.equalToSuperview().offset(-19) + $0.bottom.equalToSuperview().inset(16) } } @@ -132,6 +139,6 @@ final class HomeBakeryCollectionViewCell: UICollectionViewCell { if data.secondNearStation == "" { regionStackView.removeSecondRegion() } - regionStackView.getRegionName(data.firstNearStation, data.secondNearStation) + regionStackView.getRegionName(data.firstNearStation, data.secondNearStation ?? "") } } diff --git a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBottomCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBottomCollectionViewCell.swift index 2c3da14e..9dcec4a9 100644 --- a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBottomCollectionViewCell.swift +++ b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeBottomCollectionViewCell.swift @@ -42,9 +42,8 @@ final class HomeBottomCollectionViewCell: UICollectionViewCell { $0.textAlignment = .left $0.numberOfLines = 0 $0.basic(text: I18N.Home.bottomSectionTitle, - font: .pretendardMedium(11), + font: .captionM2!, color: .gbbGray300!) - } } diff --git a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeReviewCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeReviewCollectionViewCell.swift index c5f8a6bd..0191d382 100644 --- a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeReviewCollectionViewCell.swift +++ b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeReviewCollectionViewCell.swift @@ -16,6 +16,8 @@ final class HomeReviewCollectionViewCell: UICollectionViewCell { var updateData: ((Bool, Int) -> Void)? var index = 0 + private var keywords: [String] = [] + private var reviewList: [HomeBestReviewResponseDTO] = HomeBestReviewResponseDTO.item // MARK: - UI Property @@ -24,6 +26,7 @@ final class HomeReviewCollectionViewCell: UICollectionViewCell { private let bakeryTitle = UILabel() private let bakeryReview = UILabel() private lazy var bookMarkButton = BookmarkButton(configuration: .plain()) + private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: OptionsCollectionViewFlowLayout()) // MARK: - Life Cycle @@ -32,8 +35,10 @@ final class HomeReviewCollectionViewCell: UICollectionViewCell { setLayout() setUI() + + setRegister() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -58,24 +63,40 @@ final class HomeReviewCollectionViewCell: UICollectionViewCell { } reviewTitle.do { - $0.basic(font: .pretendardBold(14), color: .white) + $0.basic(font: .bodyB2!, color: .white) $0.numberOfLines = 2 } + collectionView.do { + $0.isScrollEnabled = false + $0.backgroundColor = .clear + $0.delegate = self + $0.dataSource = self + } + + bakeryTitle.do { + $0.basic(font: .bodyB2!, color: .gbbGray700!) + } + + bakeryReview.do { + $0.basic(font: .pretendardBold(13), color: .gbbGray700!) + } + [bakeryTitle, bakeryReview].forEach { - $0.numberOfLines = 1 - $0.textAlignment = .left - $0.basic(font: .pretendardBold(14), color: .gbbGray700!) + $0.do { + $0.numberOfLines = 1 + $0.textAlignment = .left + } } } private func setLayout() { - contentView.addSubviews(bakeryImage, bookMarkButton, bakeryReview, bakeryTitle) + contentView.addSubviews(bakeryImage, bookMarkButton, collectionView, bakeryTitle, bakeryReview) bakeryImage.addSubview(reviewTitle) bakeryImage.snp.makeConstraints { $0.top.directionalHorizontalEdges.equalToSuperview() - $0.height.equalTo(CGFloat().convertByHeightRatio(170)) + $0.height.equalTo(130) } bookMarkButton.snp.makeConstraints { @@ -85,17 +106,24 @@ final class HomeReviewCollectionViewCell: UICollectionViewCell { } reviewTitle.snp.makeConstraints { + $0.bottom.equalTo(bakeryImage.snp.bottom).inset(13) $0.directionalHorizontalEdges.equalToSuperview().inset(15) - $0.bottom.equalToSuperview().offset(-13) } - bakeryReview.snp.makeConstraints { - $0.bottom.equalToSuperview().offset(-18) - $0.leading.equalToSuperview().offset(15) + + collectionView.snp.makeConstraints { + $0.top.equalTo(bakeryImage.snp.bottom).offset(16) + $0.directionalHorizontalEdges.equalToSuperview().inset(16) + $0.height.equalTo(25) } bakeryTitle.snp.makeConstraints { - $0.bottom.equalTo(bakeryReview.snp.top).offset(-4) - $0.leading.equalToSuperview().offset(15) + $0.top.equalTo(collectionView.snp.bottom).offset(6) + $0.leading.equalToSuperview().offset(16) + } + + bakeryReview.snp.makeConstraints { + $0.top.equalTo(bakeryTitle.snp.bottom).offset(6) + $0.bottom.leading.equalToSuperview().inset(16) } } @@ -110,7 +138,45 @@ final class HomeReviewCollectionViewCell: UICollectionViewCell { guard let self = self else { return } self.updateData?(status, self.index) } - bookMarkButton.isSelected = data.isBooked ? true: false + bookMarkButton.isSelected = data.isBooked + + if !self.reviewList[index].firstMaxRecommendKeyword.isEmpty { + self.keywords.append(data.firstMaxRecommendKeyword) + } + if self.reviewList[index].secondMaxRecommendKeyword != nil { + self.keywords.append(data.secondMaxRecommendKeyword ?? "" ) + } + } +} + +// MARK: - CollectionView Register + +extension HomeReviewCollectionViewCell { + private func setRegister() { + collectionView.register(cell: DescriptionCollectionViewCell.self) + } +} + +// MARK: - UICollectionViewDataSource + +extension HomeReviewCollectionViewCell: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return keywords.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: DescriptionCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.configureTagTitle(self.keywords[indexPath.item]) + return cell + } +} + +// MARK: - UICollectionViewDelegateFlowLayout +extension HomeReviewCollectionViewCell: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let keywordsTitle = self.keywords[indexPath.item] + let itemSize = keywordsTitle.size(withAttributes: [NSAttributedString.Key.font: UIFont.pretendardMedium(13)]) + return CGSize(width: itemSize.width + 12, height: itemSize.height + 8) } } diff --git a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeViewController.swift b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeViewController.swift index 8884cd6f..6cd3e33f 100644 --- a/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/GEON-PPANG-iOS/Presentation/Home/ViewControllers/HomeViewController.swift @@ -42,7 +42,7 @@ final class HomeViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - setRegister() + setRegistration() setDataSource() setReloadData() } @@ -57,6 +57,9 @@ final class HomeViewController: BaseViewController { $0.gotoNextView = { Utils.push(self.navigationController, SearchViewController()) } + $0.addActionToFilterButton { + Utils.push(self.navigationController, FilterPurposeViewController(maxSteps: 3, username: "찐빵대빵")) + } } collectionView.do { @@ -73,7 +76,7 @@ final class HomeViewController: BaseViewController { topView.snp.makeConstraints { $0.top.equalTo(safeArea) $0.directionalHorizontalEdges.equalTo(safeArea) - $0.height.equalTo(convertByHeightRatio(200)) + $0.height.equalTo(200) } collectionView.snp.makeConstraints { @@ -85,7 +88,7 @@ final class HomeViewController: BaseViewController { // MARK: - Setting - private func setRegister() { + private func setRegistration() { collectionView.register(cell: HomeBakeryCollectionViewCell.self) collectionView.register(cell: HomeReviewCollectionViewCell.self) collectionView.register(cell: HomeBottomCollectionViewCell.self) @@ -132,9 +135,7 @@ final class HomeViewController: BaseViewController { dataSource?.supplementaryViewProvider = { (collectionView, _, indexPath) in let header: HomeHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, indexPath: indexPath) - Sections.allCases.forEach { - header.setctionHeaderTitle($0.title) - } + switch indexPath.section { case 0: header.setctionHeaderTitle(Sections.bakery.title) diff --git a/GEON-PPANG-iOS/Presentation/Home/Views/HomeHeaderView.swift b/GEON-PPANG-iOS/Presentation/Home/Views/HomeHeaderView.swift index 0c4d6c1d..9cb53c9c 100644 --- a/GEON-PPANG-iOS/Presentation/Home/Views/HomeHeaderView.swift +++ b/GEON-PPANG-iOS/Presentation/Home/Views/HomeHeaderView.swift @@ -19,7 +19,6 @@ final class HomeHeaderView: UICollectionReusableView { override init(frame: CGRect) { super.init(frame: .zero) - setUI() setLayout() } @@ -30,13 +29,6 @@ final class HomeHeaderView: UICollectionReusableView { // MAKR: - Setting - private func setUI() { - headerLabel.do { - $0.basic(font: .pretendardBold(20), color: .gbbGray700!) - - } - } - private func setLayout() { addSubview(headerLabel) @@ -47,6 +39,22 @@ final class HomeHeaderView: UICollectionReusableView { } func setctionHeaderTitle(_ section: String) { - headerLabel.text = section + let attributedString = NSMutableAttributedString( + string: section, + attributes: [ + .font: UIFont.pretendardBold(20), + .foregroundColor: UIColor.gbbGray700! + ] + ) + + attributedString.addAttributes( + [.foregroundColor: UIColor.gbbPoint1!], + range: NSRange( + location: section.count - 8, + length: 8 + ) + ) + headerLabel.attributedText = attributedString + } } diff --git a/GEON-PPANG-iOS/Presentation/Home/Views/HomeTopView.swift b/GEON-PPANG-iOS/Presentation/Home/Views/HomeTopView.swift index 26c7013d..8eeacb8f 100644 --- a/GEON-PPANG-iOS/Presentation/Home/Views/HomeTopView.swift +++ b/GEON-PPANG-iOS/Presentation/Home/Views/HomeTopView.swift @@ -42,8 +42,7 @@ final class HomeTopView: UIView { titleLabel.do { $0.numberOfLines = 2 $0.textAlignment = .left - $0.font = .pretendardBold(26) - $0.basic(font: .pretendardBold(26), + $0.basic(font: .title1!, color: .gbbGray700!) } @@ -94,4 +93,13 @@ final class HomeTopView: UIView { func setTitle(_ title: String) { titleLabel.text = "\(title)님\n건빵에 오신걸 환영해요!" } + + // MARK: - Custom Method + + func addActionToFilterButton(_ action: @escaping () -> Void) { + filterButton.addAction(UIAction { _ in + action() + }, for: .touchUpInside) + } + } diff --git a/GEON-PPANG-iOS/Presentation/MyPage/Model/MyPageDTO.swift b/GEON-PPANG-iOS/Presentation/MyPage/Model/MyPageDTO.swift new file mode 100644 index 00000000..801c559f --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyPage/Model/MyPageDTO.swift @@ -0,0 +1,28 @@ +// +// MyPageDTO.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import Foundation + +struct MyPageDTO: Codable { + let memberNickname: String + let mainPurpose: String + let breadType: BreadResponseType +} + +extension MyPageDTO { + + static func dummyData() -> MyPageDTO { + return .init(memberNickname: "Id", + mainPurpose: "HEALTH", + breadType: .init(breadTypeID: 1, + breadTypeName: "맛 • 다이어트", + isGlutenFree: true, + isVegan: true, + isNutFree: false, + isSugarFree: true)) + } +} diff --git a/GEON-PPANG-iOS/Presentation/MyPage/View/ImageWithSubtitleButton.swift b/GEON-PPANG-iOS/Presentation/MyPage/View/ImageWithSubtitleButton.swift new file mode 100644 index 00000000..2629dd5b --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyPage/View/ImageWithSubtitleButton.swift @@ -0,0 +1,97 @@ +// +// ImageWithSubtitleButton.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import UIKit + +import SnapKit +import Then + +final class ImageWithSubtitleButton: UIButton { + + // MARK: - Property + + enum ButtonType { + case bookmark + case myReview + } + + private var buttonImage: UIImage { + switch type { + case .bookmark: return .bookmarkIcon + case .myReview: return .reviewIcon + } + } + + private var buttonSubtitle: String { + switch type { + case .bookmark: return I18N.MyPage.bookmark + case .myReview: return I18N.MyPage.myReviews + } + } + + private var type: ButtonType + + // MARK: - UI Property + + private lazy var buttonImageView = UIImageView(image: buttonImage) + private lazy var buttonSubtitleLabel = UILabel() + + // MARK: - Life Cycle + + init(buttonType: ButtonType) { + self.type = buttonType + + super.init(frame: .zero) + + setLayout() + setUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + self.snp.makeConstraints { + $0.width.equalTo(66) + $0.height.equalTo(45) + } + + addSubview(buttonImageView) + buttonImageView.snp.makeConstraints { + $0.top.centerX.equalToSuperview() + $0.size.equalTo(24) + } + + addSubview(buttonSubtitleLabel) + buttonSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(buttonImageView.snp.bottom).offset(4) + $0.centerX.equalTo(buttonImageView) + } + } + + private func setUI() { + buttonSubtitleLabel.do { + $0.text = buttonSubtitle + $0.font = .captionB1 + $0.textColor = .gbbPoint1 + } + } + + // MARK: - Custom Method + + func addButtonAction(_ tapped: @escaping () -> Void) { + let action = UIAction { _ in + tapped() + } + self.addAction(action, for: .touchUpInside) + } + +} diff --git a/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewCell.swift new file mode 100644 index 00000000..593bf13d --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewCell.swift @@ -0,0 +1,103 @@ +// +// MyPageCollectionViewCell.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import UIKit + +import SnapKit +import Then + +final class MyPageCollectionViewCell: UICollectionViewCell { + + // MARK: - UI Property + + private let titleLabel = UILabel() + private let rightChevronImageViewContainer = UIView() + private let rightChevronImageView = UIImageView(image: .rightArrowIcon) + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + contentView.addSubview(titleLabel) + titleLabel.snp.makeConstraints { + $0.leading.equalToSuperview().inset(24) + $0.centerY.equalToSuperview() + } + + contentView.addSubview(rightChevronImageViewContainer) + rightChevronImageViewContainer.snp.makeConstraints { + $0.verticalEdges.equalToSuperview().inset(10) + $0.width.height.equalTo(48) + $0.trailing.equalToSuperview().inset(6) + } + + rightChevronImageViewContainer.addSubview(rightChevronImageView) + rightChevronImageView.snp.makeConstraints { + $0.center.equalToSuperview() + } + } + + private func setUI() { + contentView.do { + $0.backgroundColor = .gbbWhite + } + + titleLabel.do { + $0.font = .bodyM1 + $0.textColor = .gbbBlack + } + } + + // MARK: - Custom Method + + func configureTitle(to text: String) { + titleLabel.do { + $0.text = text + } + } + + func applyTopThickBorder() { + let borderView = UIView() + contentView.addSubview(borderView) + borderView.snp.makeConstraints { + $0.bottom.equalTo(contentView.snp.top) + $0.height.equalTo(8) + $0.horizontalEdges.equalToSuperview() + } + + borderView.do { + $0.backgroundColor = .gbbGray200 + } + } + + func applyTopThinBorder() { + let borderView = UIView() + contentView.addSubview(borderView) + borderView.snp.makeConstraints { + $0.height.equalTo(1) + $0.horizontalEdges.equalToSuperview() + } + + borderView.do { + $0.backgroundColor = .gbbGray200 + } + } + +} diff --git a/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewFooter.swift b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewFooter.swift new file mode 100644 index 00000000..4442dbdb --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewFooter.swift @@ -0,0 +1,68 @@ +// +// MyPageCollectionViewFooter.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import UIKit + +import SnapKit +import Then + +final class MyPageCollectionViewFooter: UICollectionReusableView { + + // MARK: - UI Property + + private let appVersionLabel = UILabel() + private let appVersionNumLabel = UILabel() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + addSubview(appVersionLabel) + appVersionLabel.snp.makeConstraints { + $0.leading.equalToSuperview().inset(24) + $0.top.equalToSuperview().inset(12) + } + + addSubview(appVersionNumLabel) + appVersionNumLabel.snp.makeConstraints { + $0.trailing.equalToSuperview().inset(24) + $0.centerY.equalTo(appVersionLabel) + } + } + + private func setUI() { + self.do { + $0.backgroundColor = .gbbWhite + } + + appVersionLabel.do { + $0.text = I18N.MyPage.appVersion + $0.font = .captionM1 + $0.textColor = .gbbMain1 + } + + appVersionNumLabel.do { + $0.text = I18N.MyPage.appVersionNum + $0.font = .captionM1 + $0.textColor = .gbbMain1 + } + } + +} diff --git a/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewHeader.swift b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewHeader.swift new file mode 100644 index 00000000..6554da3c --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPageCollectionViewHeader.swift @@ -0,0 +1,236 @@ +// +// MyPageCollectionViewHeader.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import UIKit + +import SnapKit +import Then + +final class MyPageCollectionViewHeader: UICollectionReusableView { + + // MARK: - Prperty + + private let myPageData = MyPageDTO.dummyData() + private let username = "빵순이빵돌이" + private lazy var myPageTagData = myPageData.breadType.configureTrueOptions() + var svedBakeryTapped: (() -> Void)? + var myReviewsTapped: (() -> Void)? + + // MARK: - UI Property + + private let mainTitleLabel = UILabel() + private let mainTitleLogoImageView = UIImageView(image: .enabledStorelistIcon) + private let profileImageViewContainer = UIView() + private let profileImageView = UIImageView(image: .profileIcon) + private let purposeFilterChipView = MyPagePurposeChipView() + private let userNameLabel = UILabel() + private let flowLayout = OptionsCollectionViewFlowLayout() + lazy var filterCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + private let rightChevronButton = UIButton() + + private let buttonsContainer = UIView() + private let bookmarkButton = ImageWithSubtitleButton(buttonType: .bookmark) + private let myReviewButton = ImageWithSubtitleButton(buttonType: .myReview) + private lazy var buttonsStackView = UIStackView(arrangedSubviews: [bookmarkButton, myReviewButton]) + private let seperatorView = UIView() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + setDelegate() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + super.layoutSubviews() + + Utils.updateCollectionViewConstraint(of: filterCollectionView) + } + + // MARK: - Setting + + private func setLayout() { + addSubview(mainTitleLabel) + mainTitleLabel.snp.makeConstraints { + // TODO: notch 유무로 재계산 + $0.top.equalToSuperview().inset(44) + $0.leading.equalToSuperview().inset(24) + } + + addSubview(mainTitleLogoImageView) + mainTitleLogoImageView.snp.makeConstraints { + $0.leading.equalTo(mainTitleLabel.snp.trailing).offset(8) + $0.centerY.equalTo(mainTitleLabel) + $0.size.equalTo(24) + } + + addSubview(profileImageViewContainer) + profileImageViewContainer.snp.makeConstraints { + $0.leading.equalTo(mainTitleLabel) + $0.top.equalTo(mainTitleLabel.snp.bottom).offset(34) + $0.size.equalTo(73) + } + + profileImageViewContainer.addSubview(profileImageView) + profileImageView.snp.makeConstraints { + $0.center.equalToSuperview() + $0.size.equalTo(36) + } + + addSubview(purposeFilterChipView) + purposeFilterChipView.snp.makeConstraints { + $0.leading.equalTo(profileImageViewContainer.snp.trailing).offset(10) + $0.top.equalTo(profileImageViewContainer) + } + + addSubview(userNameLabel) + userNameLabel.snp.makeConstraints { + $0.leading.equalTo(purposeFilterChipView) + $0.top.equalTo(purposeFilterChipView.snp.bottom).offset(14) + } + + addSubview(filterCollectionView) + filterCollectionView.snp.makeConstraints { + $0.leading.equalTo(purposeFilterChipView) + $0.top.equalTo(userNameLabel.snp.bottom).offset(10) + $0.height.equalTo(60) + $0.width.equalTo(170) + } + + addSubview(rightChevronButton) + rightChevronButton.snp.makeConstraints { + $0.top.equalTo(purposeFilterChipView) + $0.trailing.equalToSuperview().inset(5) + $0.bottom.equalTo(filterCollectionView) + $0.width.equalTo(48) + } + + addSubview(buttonsContainer) + buttonsContainer.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview().inset(24) + $0.top.equalTo(filterCollectionView.snp.bottom).offset(24) + $0.height.equalTo(87) + $0.bottom.equalToSuperview().inset(20) + } + + addSubview(buttonsStackView) + buttonsStackView.snp.makeConstraints { + $0.center.equalTo(buttonsContainer) + } + + addSubview(seperatorView) + seperatorView.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.centerY.equalTo(buttonsContainer) + $0.width.equalTo(1) + $0.height.equalTo(42) + } + } + + private func setUI() { + self.do { + $0.backgroundColor = .gbbWhite + } + + mainTitleLabel.do { + $0.text = I18N.MyPage.title + $0.font = .title1 + $0.textColor = .gbbGray700 + } + + profileImageViewContainer.do { + $0.backgroundColor = .gbbBackground2 + $0.makeCornerRound(radius: 36.5) + } + + userNameLabel.do { + $0.text = username + $0.font = .title2 + $0.textColor = .gbbGray700 + } + + flowLayout.do { + $0.minimumLineSpacing = 10 + $0.minimumInteritemSpacing = 4 + } + + filterCollectionView.do { + $0.register(cell: DescriptionCollectionViewCell.self) + } + + rightChevronButton.do { + $0.setImage(.rightArrowIcon, for: .normal) + } + + buttonsContainer.do { + $0.makeCornerRound(radius: 12) + $0.makeBorder(width: 1, color: .gbbPoint1!) + $0.backgroundColor = .gbbBackground1 + } + + buttonsStackView.do { + $0.axis = .horizontal + $0.spacing = CGFloat().convertByWidthRatio(96) + } + + seperatorView.do { + $0.backgroundColor = .gbbPoint1 + } + bookmarkButton.do { + $0.addButtonAction { + self.svedBakeryTapped?() + } + } + myReviewButton.do { + $0.addButtonAction { + self.myReviewsTapped?() + } + } + } + + private func setDelegate() { + filterCollectionView.delegate = self + filterCollectionView.dataSource = self + } + + // MARK: - Custom Method + + func addNextButtonAction(_ action: @escaping () -> Void) { + rightChevronButton.addAction(UIAction { _ in + action() + }, for: .touchUpInside) + } + +} + +// MARK: - UICollectionViewDelegate extension + +extension MyPageCollectionViewHeader: UICollectionViewDelegate {} + +// MARK: - UICollectionViewDataSource extension + +extension MyPageCollectionViewHeader: UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return myPageTagData.filter { $0.1 == true }.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: DescriptionCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.configureTagTitle(myPageTagData[indexPath.item].0) + return cell + } + +} diff --git a/GEON-PPANG-iOS/Presentation/MyPage/View/MyPagePurposeChipView.swift b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPagePurposeChipView.swift new file mode 100644 index 00000000..fbfee91a --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyPage/View/MyPagePurposeChipView.swift @@ -0,0 +1,63 @@ +// +// MyPagePurposeChipView.swift +// GEON-PPANG-iOS +// +// Created by 이성민 on 2023/07/14. +// + +import UIKit + +import SnapKit +import Then + +final class MyPagePurposeChipView: UIView { + + // MARK: - Property + + // TODO: filterPurposeType 으로 변경 + let purposeType = "맛 • 다이어트" + + // MARK: - UI Property + + private let purposeLabel = UILabel() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + addSubview(purposeLabel) + purposeLabel.snp.makeConstraints { + $0.horizontalEdges.equalToSuperview().inset(11) + $0.verticalEdges.equalToSuperview().inset(4) + } + } + + private func setUI() { + self.do { + $0.backgroundColor = .gbbPoint2 + $0.makeCornerRound(radius: 12.5) + $0.makeBorder(width: 0.5, color: .gbbPoint1!) + } + + purposeLabel.do { + // TODO: filterPurposeType 으로 변환 후 .rawValue 사용 + $0.text = purposeType + $0.font = .captionM1 + $0.textColor = .gbbPoint1 + } + } + +} diff --git a/GEON-PPANG-iOS/Presentation/MyPage/ViewControllers/MyPageViewController.swift b/GEON-PPANG-iOS/Presentation/MyPage/ViewControllers/MyPageViewController.swift index df7ef2a7..0c1e0cfb 100644 --- a/GEON-PPANG-iOS/Presentation/MyPage/ViewControllers/MyPageViewController.swift +++ b/GEON-PPANG-iOS/Presentation/MyPage/ViewControllers/MyPageViewController.swift @@ -7,10 +7,158 @@ import UIKit -final class MyPageViewController: UIViewController { +import SnapKit +import Then - override func viewDidLoad() { - super.viewDidLoad() +final class MyPageViewController: BaseViewController { + + // MARK: - UI Property + + private let flowLayout = UICollectionViewFlowLayout() + private lazy var myPageCollectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout) + + // MARK: - Life Cycle + + override func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + + Utils.updateCollectionViewConstraint(of: myPageCollectionView) + } + + // MARK: - Setting + + override func setLayout() { + view.addSubview(myPageCollectionView) + myPageCollectionView.snp.makeConstraints { + $0.top.equalTo(view.safeAreaLayoutGuide) + $0.horizontalEdges.bottom.equalToSuperview() + $0.height.equalTo(50) + } + } + + override func setUI() { + + flowLayout.do { + $0.scrollDirection = .vertical + $0.sectionInset = .init(top: 8, left: 0, bottom: 0, right: 0) + $0.minimumLineSpacing = 1 + } + + myPageCollectionView.do { + $0.register(header: MyPageCollectionViewHeader.self) + $0.register(cell: MyPageCollectionViewCell.self) + $0.register(footer: MyPageCollectionViewFooter.self) + } + } + + override func setDelegate() { + myPageCollectionView.delegate = self + myPageCollectionView.dataSource = self + } + +} +// MARK: - UICollectionViewDelegate extension + +extension MyPageViewController: UICollectionViewDelegate {} + +// MARK: - UICollectionViewDataSource extension + +extension MyPageViewController: UICollectionViewDataSource { + + func numberOfSections(in collectionView: UICollectionView) -> Int { + return MyPageSectionEnum.allCases.count + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + switch section { + case 0: return MyPageSectionEnum.terms.items.count + case 1: return MyPageSectionEnum.questions.items.count + default: return 0 + } + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: MyPageCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + let section = indexPath.section + switch section { + case 0: + cell.configureTitle(to: MyPageSectionEnum.terms.items[indexPath.item]) + cell.applyTopThickBorder() + case 1: + cell.configureTitle(to: MyPageSectionEnum.questions.items[indexPath.item]) + if indexPath.item == 0 { + cell.applyTopThickBorder() + } else { + cell.applyTopThinBorder() + } + default: return UICollectionViewCell() + } + return cell + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + switch indexPath.section { + case 0: + let header: MyPageCollectionViewHeader = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, indexPath: indexPath) + header.addNextButtonAction { + Utils.push(self.navigationController, FilterPurposeViewController(maxSteps: 3, username: "찐빵대빵")) + } + header.myReviewsTapped = { + Utils.push(self.navigationController, MyReviewsViewController()) + } + header.svedBakeryTapped = { + Utils.push(self.navigationController, MySavedBakeryViewController()) + } + return header + case 1: + let footer: MyPageCollectionViewFooter = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, indexPath: indexPath) + return footer + default: + return UICollectionReusableView() + } + } + +} + +// MARK: - UICollectionViewDelegateFlowLayout extension + +extension MyPageViewController: UICollectionViewDelegateFlowLayout { + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + switch indexPath.section { + case 0: + return .init(width: SizeLiteral.Screen.width, height: 68 + 8) + case 1: + if indexPath.item == 0 { + return .init(width: SizeLiteral.Screen.width, height: 68 + 8) + } else { + return .init(width: SizeLiteral.Screen.width, height: 68 + 1) + } + default: + return .zero + } + return .init(width: SizeLiteral.Screen.width, height: 68) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + let indexPath = IndexPath(item: 0, section: section) + let header: MyPageCollectionViewHeader = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, indexPath: indexPath) + switch section { + case 0: return header.systemLayoutSizeFitting(.init(width: collectionView.frame.width, + height: UIView.layoutFittingExpandedSize.height), + withHorizontalFittingPriority: .required, + verticalFittingPriority: .fittingSizeLevel) + default: return .zero + } + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { + switch section { + case 0: return .zero + case 1: return .init(width: SizeLiteral.Screen.width, height: 60) + default: return .zero + } } + } diff --git a/GEON-PPANG-iOS/Presentation/MyReviews/ViewControllers/MyReviewsViewController.swift b/GEON-PPANG-iOS/Presentation/MyReviews/ViewControllers/MyReviewsViewController.swift new file mode 100644 index 00000000..b901ded2 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyReviews/ViewControllers/MyReviewsViewController.swift @@ -0,0 +1,147 @@ +// +// MyReviewsViewController.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class MyReviewsViewController: BaseViewController { + + // MARK: - Property + + private lazy var safeArea = self.view.safeAreaLayoutGuide + private var myReviewslist: [BakeryListResponseDTO] = BakeryListResponseDTO.item + + // MARK: - UI Property + + private let naviView = CustomNavigationBar() + private lazy var collectionView = UICollectionView(frame: .zero, + collectionViewLayout: layout()) + + // MARK: - Setting + + override func viewDidLoad() { + super.viewDidLoad() + + setRegistration() + } + + override func setLayout() { + view.addSubviews(naviView, collectionView) + + naviView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.directionalHorizontalEdges.equalTo(safeArea) + } + + collectionView.snp.makeConstraints { + $0.top.equalTo(naviView.snp.bottom) + $0.directionalHorizontalEdges.equalTo(safeArea) + $0.bottom.equalToSuperview() + } + } + + override func setUI() { + naviView.do { + $0.addBackButtonAction(UIAction { _ in + self.navigationController?.popViewController(animated: true) + }) + $0.configureLeftTitle(to: I18N.MyReviews.naviTitle) + } + } + + override func setDelegate() { + collectionView.delegate = self + collectionView.dataSource = self + } + + private func setRegistration() { + collectionView.register(cell: BakeryListCollectionViewCell.self) + collectionView.register(cell: EmptyCollectionViewCell.self) + collectionView.register(header: MyReviewsHeaderView.self) + } + + private func layout() -> UICollectionViewLayout { + let layout = UICollectionViewCompositionalLayout {_, layoutEnvirnment in + if self.myReviewslist.isEmpty { + return self.normalSection() + } else { + var config = UICollectionLayoutListConfiguration(appearance: .grouped) + config.backgroundColor = .clear + config.showsSeparators = true + config.headerMode = .supplementary + config.separatorConfiguration.topSeparatorVisibility = .hidden + config.separatorConfiguration.bottomSeparatorVisibility = .visible + + let layoutSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvirnment) + return layoutSection + } + } + return layout + } + + private func normalSection() -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init( + widthDimension: .fractionalWidth(1), + heightDimension: .fractionalHeight(1)) + ) + let group = NSCollectionLayoutGroup.vertical( + layoutSize: .init( + widthDimension: .fractionalWidth(1), + heightDimension: .fractionalHeight(1) + ), + subitem: item, + count: 1 + ) + let section = NSCollectionLayoutSection(group: group) + return section + } +} + +// MARK: - UICollectionViewDataSource + +extension MyReviewsViewController: UICollectionViewDataSource { + + func numberOfSections(in collectionView: UICollectionView) -> Int { + if myReviewslist.isEmpty { + return 1 + } else { + return myReviewslist.count + } + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return 1 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + if myReviewslist.isEmpty { + let cell: EmptyCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.noReview) + return cell + } else { + let cell: BakeryListCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.reviewType) + cell.updateUI(data: myReviewslist[indexPath.section], index: indexPath.item) + return cell + } + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension MyReviewsViewController: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + if myReviewslist.isEmpty { + return UICollectionReusableView() + } else { + let header: MyReviewsHeaderView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, indexPath: indexPath) + return header + } + } +} diff --git a/GEON-PPANG-iOS/Presentation/MyReviews/Views/MyReviewsHeaderView.swift b/GEON-PPANG-iOS/Presentation/MyReviews/Views/MyReviewsHeaderView.swift new file mode 100644 index 00000000..7ac03baf --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MyReviews/Views/MyReviewsHeaderView.swift @@ -0,0 +1,63 @@ +// +// MyReviewsHeaderView.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/14. +// + +import UIKit + +import SnapKit +import Then + +final class MyReviewsHeaderView: UICollectionReusableView { + + // MARK: - UI Property + + private let dateLabel = UILabel() + private lazy var dotButton = UIButton() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setLayout() { + self.addSubviews(dateLabel, dotButton) + + dateLabel.snp.makeConstraints { + $0.leading.equalToSuperview().offset(24) + $0.centerY.equalToSuperview() + } + + dotButton.snp.makeConstraints { + $0.size.equalTo(24) + $0.directionalVerticalEdges.equalToSuperview() + $0.trailing.equalToSuperview().inset(24) + } + } + + private func setUI() { + dateLabel.do { + $0.basic(text: "23.08.09", + font: .captionM1!, + color: .gbbGray400!) + } + + dotButton.do { + $0.setImage(.dotdotdotIcon, for: .normal) + $0.addAction(UIAction { _ in + print("myreviews Tapped") + }, for: .touchUpInside) + } + } + +} diff --git a/GEON-PPANG-iOS/Presentation/MySavedBakery/ViewControllers/MySavedBakeryViewController.swift b/GEON-PPANG-iOS/Presentation/MySavedBakery/ViewControllers/MySavedBakeryViewController.swift new file mode 100644 index 00000000..9082472a --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/MySavedBakery/ViewControllers/MySavedBakeryViewController.swift @@ -0,0 +1,154 @@ +// +// MySavedBakeryViewController.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class MySavedBakeryViewController: BaseViewController { + + // MARK: - Property + + enum Section { + case main, empty + } + typealias DataSource = UICollectionViewDiffableDataSource + private var dataSource: DataSource? + private var savedList: [BakeryListResponseDTO] = BakeryListResponseDTO.item + private var currentSection: [Section] = [.empty] + + private lazy var safeArea = self.view.safeAreaLayoutGuide + + // MARK: - UI Property + + private let naviView = CustomNavigationBar() + private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + + // MARK: - Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + + setRegistration() + setDataSource() + setReloadData() + } + + // MARK: - Setting + + override func setLayout() { + + view.addSubviews(naviView, collectionView) + + naviView.snp.makeConstraints { + $0.top.equalToSuperview() + $0.directionalHorizontalEdges.equalTo(safeArea) + } + + collectionView.snp.makeConstraints { + $0.top.equalTo(naviView.snp.bottom) + $0.leading.equalTo(safeArea).offset(-24) + $0.trailing.equalTo(safeArea) + $0.bottom.equalToSuperview() + } + } + + override func setUI() { + naviView.do { + $0.addBackButtonAction(UIAction { _ in + dump(self.navigationController) + self.navigationController?.popViewController(animated: true) + }) + $0.configureLeftTitle(to: I18N.MySavedBakery.naviTitle) + } + } + + private func setRegistration() { + collectionView.register(cell: BakeryListCollectionViewCell.self) + collectionView.register(cell: EmptyCollectionViewCell.self) + } + + private func setDataSource() { + dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in + let section = self.dataSource?.snapshot().sectionIdentifiers[indexPath.section] + switch section { + case .main: + let cell: BakeryListCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.defaultType) + if let bakeryListItem = item as? BakeryListResponseDTO { + cell.updateUI(data: bakeryListItem, index: indexPath.item) + } + return cell + case .empty, .none: + let cell: EmptyCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.noBookmark) + return cell + } + }) + } + + private func setReloadData() { + var snapshot = NSDiffableDataSourceSnapshot() + defer { dataSource?.apply(snapshot, animatingDifferences: false)} + snapshot.appendSections([.main]) + snapshot.appendItems(savedList) + } + + private func updateDataSource(data: BakeryListResponseDTO) { + guard var snapshot = dataSource?.snapshot() else { return } + if data.bookmarkCount == 0 { + snapshot.deleteSections(currentSection) + snapshot.appendItems([0], toSection: .empty) + currentSection = [.empty] + dataSource?.apply(snapshot) + } else { + snapshot.deleteSections(currentSection) + snapshot.appendItems(savedList, toSection: .main) + currentSection = [.main] + dataSource?.apply(snapshot) + } + } + + private func layout() -> UICollectionViewLayout { + + let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvirnment in + let section = self.dataSource?.snapshot().sectionIdentifiers[sectionIndex] + switch section { + case .main: + var config = UICollectionLayoutListConfiguration(appearance: .plain) + config.backgroundColor = .clear + config.showsSeparators = true + + let layoutSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvirnment) + return layoutSection + case .empty, .none: + return self.normalSection() + } + } + + return layout + } + + private func normalSection() -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init( + widthDimension: .fractionalWidth(1), + heightDimension: .fractionalHeight(1)) + ) + let group = NSCollectionLayoutGroup.vertical( + layoutSize: .init( + widthDimension: .fractionalWidth(1), + heightDimension: .fractionalHeight(1) + ), + subitem: item, + count: 1 + ) + let section = NSCollectionLayoutSection(group: group) + + return section + } +} diff --git a/GEON-PPANG-iOS/Presentation/Onboarding/OnboardingViewController.swift b/GEON-PPANG-iOS/Presentation/Onboarding/OnboardingViewController.swift new file mode 100644 index 00000000..fece6113 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Onboarding/OnboardingViewController.swift @@ -0,0 +1,125 @@ +// +// OnboardingViewController.swift +// GEON-PPANG-iOS +// +// Created by kyun on 2023/07/11. +// + +import UIKit + +import SnapKit +import Then + +final class OnboardingViewController: BaseViewController { + + // MARK: - UI Property + + private let logoImage = UIImageView() + private lazy var signinButton = CommonButton() + private lazy var signupButton = CommonButton() + private let latelySigninView = DrawDashLineView() + private let latelySigninLabel = UILabel() + private lazy var kakaoButton = UIButton() + private lazy var appleButton = UIButton() + private lazy var naverButton = UIButton() + private lazy var googleButton = UIButton() + + // MARK: - Setting + + override func setUI() { + + logoImage.do { + $0.image = .launchscreenIcon + $0.contentMode = .scaleAspectFit + } + + signinButton.do { + $0.getButtonTitle(.login) + $0.getButtonUI(.gbbMain2!) + } + + signupButton.do { + $0.getButtonTitle(.signIn) + $0.getButtonUI(.gbbWhite!, .gbbGray300) + } + + latelySigninLabel.do { + $0.text = I18N.Onboarding.latelySigninText + $0.font = .captionB1 + $0.textColor = .gbbGray500 + $0.textAlignment = .center + $0.partColorChange(targetString: I18N.Onboarding.latelySigninSNS, textColor: .gbbMain1!) // 특정 문자열의 textColor를 변경 + } + + kakaoButton.do { + $0.setImage(.kakaoLoginButton, for: .normal) + } + + appleButton.do { + $0.setImage(.appleLoginButton, for: .normal) + } + + naverButton.do { + $0.setImage(.naverLoginButton, for: .normal) + } + + googleButton.do { + $0.setImage(.googleLoginButton, for: .normal) + } + } + + override func setLayout() { + + view.addSubviews(logoImage, signinButton, signupButton, latelySigninView, kakaoButton, appleButton, naverButton, googleButton) + + latelySigninView.addSubview(latelySigninLabel) + + logoImage.snp.makeConstraints { + $0.top.equalToSuperview().inset(convertByHeightRatio(190)) + $0.centerX.equalToSuperview() + $0.height.equalTo(convertByHeightRatio(179)) + } + + signinButton.snp.makeConstraints { + $0.top.equalTo(logoImage.snp.bottom).offset(convertByHeightRatio(71)) + $0.directionalHorizontalEdges.equalToSuperview().inset(24) + $0.height.equalTo(convertByHeightRatio(56)) + } + + signupButton.snp.makeConstraints { + $0.top.equalTo(signinButton.snp.bottom).offset(convertByHeightRatio(20)) + $0.directionalHorizontalEdges.equalToSuperview().inset(24) + $0.height.equalTo(convertByHeightRatio(56)) + } + + latelySigninView.snp.makeConstraints { + $0.top.equalTo(signupButton.snp.bottom).offset(convertByHeightRatio(65)) + $0.directionalHorizontalEdges.equalToSuperview().inset(77) + $0.height.equalTo(convertByHeightRatio(30)) + } + + latelySigninLabel.snp.makeConstraints { + $0.center.equalToSuperview() + } + + kakaoButton.snp.makeConstraints { + $0.top.equalTo(latelySigninView.snp.bottom).offset(convertByHeightRatio(23)) + $0.leading.equalToSuperview().inset(convertByWidthRatio(45)) + } + + appleButton.snp.makeConstraints { + $0.top.equalTo(kakaoButton.snp.top) + $0.leading.equalTo(kakaoButton.snp.trailing).offset(convertByWidthRatio(20)) + } + + naverButton.snp.makeConstraints { + $0.top.equalTo(appleButton.snp.top) + $0.leading.equalTo(appleButton.snp.trailing).offset(convertByWidthRatio(20)) + } + + googleButton.snp.makeConstraints { + $0.top.equalTo(naverButton.snp.top) + $0.leading.equalTo(naverButton.snp.trailing).offset(convertByWidthRatio(20)) + } + } +} diff --git a/GEON-PPANG-iOS/Presentation/Search/Model/SearchResponseDTO.swift b/GEON-PPANG-iOS/Presentation/Search/Model/SearchResponseDTO.swift new file mode 100644 index 00000000..ba519acd --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Search/Model/SearchResponseDTO.swift @@ -0,0 +1,43 @@ +// +// SearchResponseDTO.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/14. +// + +import Foundation + +// MARK: - SearchResponseDTO + +struct SearchResponseDTO: Hashable { + let resultCount: Int + let bakeryList: [SearchBakeryList] +} + +// MARK: - SearchBakeryList + +struct SearchBakeryList: Hashable, BakeryListProtocol { + let bakeryID: Int + let bakeryName: String + let isHACCP: Bool + let isVegan: Bool + let isNonGMO: Bool + var breadType: BreadResponseType + let firstNearStation: String + var secondNearStation: String? + let isBooked: Bool + let bookmarkCount: Int + let bakeryPicture: String +} + +extension SearchResponseDTO { + static let item: [SearchResponseDTO] = [SearchResponseDTO(resultCount: 0, bakeryList: SearchBakeryList.searchBakeryItem)] +} + +extension SearchBakeryList { + static let searchBakeryItem: [SearchBakeryList] = [SearchBakeryList(bakeryID: 1, bakeryName: "히히히히", isHACCP: true, isVegan: true, isNonGMO: true, breadType: .searchBreadType, firstNearStation: "헤헤", secondNearStation: "두번째역", isBooked: true, bookmarkCount: 5, bakeryPicture: "")] +} + +extension BreadResponseType { + static let searchBreadType: BreadResponseType = BreadResponseType(breadTypeID: 3, breadTypeName: "글루텐프리", isGlutenFree: true, isVegan: true, isNutFree: false, isSugarFree: true) +} diff --git a/GEON-PPANG-iOS/Presentation/Search/ViewControllers/EmptyCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/Search/ViewControllers/EmptyCollectionViewCell.swift new file mode 100644 index 00000000..117ac4e9 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Search/ViewControllers/EmptyCollectionViewCell.swift @@ -0,0 +1,104 @@ +// +// EmptyCollectionViewCell.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +enum EmptyType: String { + case noReview = "작성된 리뷰가 없어요!" + case noBookmark = "저장 목록이 없어요!" + case noSearch = "검색결과가 없어요\n다른 키워드로 검색해보세요!" + case initialize = "궁금하신 건빵집을\n검색해보세요!" + + var icon: UIImage { + switch self { + case .noReview: return .noReviewImage + case .noBookmark: return .noBookmarkImage + case .noSearch: return .noSearchResultImage + case .initialize: return .searchImage + } + } +} + +final class EmptyCollectionViewCell: UICollectionViewCell { + + // MARK: - Property + + private var emptyType: EmptyType = .initialize + + // MARK: - UI Property + + private let emptyIcon = UIImageView() + private let emptyLabel = UILabel() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + addSubviews(emptyIcon, emptyLabel) + + emptyIcon.snp.makeConstraints { + $0.size.equalTo(CGSize(width: 154, height: 132)) + $0.centerX.equalToSuperview() + } + + emptyLabel.snp.makeConstraints { + $0.top.equalTo(emptyIcon.snp.bottom).offset(20) + $0.centerX.equalToSuperview() + + switch emptyType { + case .initialize, .noSearch: + $0.centerY.equalToSuperview().inset(-33) + case .noReview, .noBookmark: + $0.centerY.equalToSuperview().inset(-22) + } + } + } + + private func setUI() { + emptyIcon.do { + $0.contentMode = .scaleAspectFit + } + + emptyLabel.do { + $0.numberOfLines = 0 + $0.textAlignment = .center + $0.basic(font: .title2!, color: .gbbGray300!) + } + } + + func getViewType(_ type: EmptyType) { + emptyType = type + emptyIcon.image = type.icon + emptyLabel.text = type.rawValue + + switch type { + case .initialize, .noBookmark, .noReview: + return emptyLabel.basic(text: emptyType.rawValue, font: .title2!, color: .gbbGray300!) + case .noSearch: + return emptyLabel.partFontChange(targetString: "다른 키워드로 검색해보세요!", font: .subHead!) + } + } + + func getEmtyText(_ text: String) { + emptyLabel.text = text + } +} diff --git a/GEON-PPANG-iOS/Presentation/Search/ViewControllers/SearchViewController.swift b/GEON-PPANG-iOS/Presentation/Search/ViewControllers/SearchViewController.swift index abe039ad..f9327df1 100644 --- a/GEON-PPANG-iOS/Presentation/Search/ViewControllers/SearchViewController.swift +++ b/GEON-PPANG-iOS/Presentation/Search/ViewControllers/SearchViewController.swift @@ -7,11 +7,171 @@ import UIKit -final class SearchViewController: UIViewController { +import SnapKit +import Then +final class SearchViewController: BaseViewController { + + // MARK: - Property + + enum Section { + case initial, main, empty + } + private lazy var safeArea = self.view.safeAreaLayoutGuide + typealias Item = AnyHashable + typealias DataSource = UICollectionViewDiffableDataSource + private var dataSource: DataSource? + private var searchList: [SearchResponseDTO] = SearchResponseDTO.item + private var searchBakeryList: [SearchBakeryList] = SearchBakeryList.searchBakeryItem + private var currentSection: [Section] = [.initial] + + // MARK: - UI Property + + private let naviView = SearchNavigationView() + private let searchResultView = SearchResultView() + private lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout()) + + // MARK: - Life Cycle + override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .purple + setRegistration() + setDataSource() + setReloadData() + } + + // MARK: - Setting + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + + self.view.endEditing(true) + } + + override func setLayout() { + view.addSubviews(naviView, searchResultView, collectionView) + + naviView.snp.makeConstraints { + $0.top.equalTo(safeArea) + $0.directionalHorizontalEdges.equalTo(safeArea) + $0.height.equalTo(convertByHeightRatio(68)) + } + + searchResultView.snp.makeConstraints { + $0.top.equalTo(naviView.snp.bottom) + $0.directionalHorizontalEdges.equalTo(safeArea) + $0.height.equalTo(convertByHeightRatio(55)) + } + + collectionView.snp.makeConstraints { + $0.top.equalTo(searchResultView.snp.bottom) + $0.directionalHorizontalEdges.equalTo(safeArea) + $0.bottom.equalToSuperview() + } + } + + override func setUI() { + naviView.do { + $0.dismissClosure = { + self.navigationController?.popViewController(animated: true) + } + } + searchResultView.do { + $0.updateUI(count: 3) + $0.isHidden = true + } + collectionView.do { + $0.backgroundColor = .clear + $0.isScrollEnabled = false + } + } + + private func setRegistration() { + collectionView.register(cell: EmptyCollectionViewCell.self) + collectionView.register(cell: BakeryListCollectionViewCell.self) + } + + private func setDataSource() { + dataSource = DataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in + let section = self.dataSource?.snapshot().sectionIdentifiers[indexPath.section] + switch section { + case .initial: + let cell: EmptyCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.initialize) + return cell + case .empty: + let cell: EmptyCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.noSearch) + return cell + case .main, .none: + let cell: BakeryListCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) + cell.getViewType(.defaultType) + if let searchBakeryItem = item as? SearchBakeryList { + cell.updateUI(data: searchBakeryItem, index: indexPath.item) + } + return cell + } + }) + } + + private func setReloadData() { + var snapshot = NSDiffableDataSourceSnapshot() + defer { dataSource?.apply(snapshot, animatingDifferences: false)} + snapshot.appendSections([.initial]) + snapshot.appendItems([0]) + } + + private func updateDataSource(data: SearchResponseDTO) { + guard var snapshot = dataSource?.snapshot() else { return } + if data.resultCount == 0 { + searchResultView.isHidden = true + snapshot.deleteSections(currentSection) + snapshot.appendItems([0], toSection: .empty) + currentSection = [.empty] + dataSource?.apply(snapshot) + } else { + searchResultView.isHidden = false + snapshot.deleteSections(currentSection) + snapshot.appendItems(searchBakeryList, toSection: .main) + currentSection = [.main] + dataSource?.apply(snapshot) + } + } + + private func layout() -> UICollectionViewLayout { + + let layout = UICollectionViewCompositionalLayout { sectionIndex, layoutEnvirnment in + let section = self.dataSource?.snapshot().sectionIdentifiers[sectionIndex] + switch section { + case .main: + var config = UICollectionLayoutListConfiguration(appearance: .plain) + config.backgroundColor = .clear + config.showsSeparators = true + + let layoutSection = NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvirnment) + return layoutSection + case .initial, .empty, .none: + return self.normalSection() + } + } + return layout + } + + private func normalSection() -> NSCollectionLayoutSection { + let item = NSCollectionLayoutItem(layoutSize: .init( + widthDimension: .fractionalWidth(1), + heightDimension: .fractionalHeight(1)) + ) + let group = NSCollectionLayoutGroup.vertical( + layoutSize: .init( + widthDimension: .fractionalWidth(1), + heightDimension: .fractionalHeight(1) + ), + subitem: item, + count: 1 + ) + let section = NSCollectionLayoutSection(group: group) + return section } + } diff --git a/GEON-PPANG-iOS/Presentation/Search/Views/SearchNavigationView.swift b/GEON-PPANG-iOS/Presentation/Search/Views/SearchNavigationView.swift new file mode 100644 index 00000000..1a42b678 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Search/Views/SearchNavigationView.swift @@ -0,0 +1,66 @@ +// +// SearchNavigationView.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class SearchNavigationView: UIView { + + // MARK: - Property + + var dismissClosure: (() -> Void)? + + // MARK: - UI Property + + private lazy var backButton = UIButton() + private let searchTextField = SearchTextField() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + addSubviews(backButton, searchTextField) + + backButton.snp.makeConstraints { + $0.top.equalToSuperview().offset(10) + $0.leading.equalToSuperview().offset(5) + $0.size.equalTo(48) + } + + searchTextField.snp.makeConstraints { + $0.directionalVerticalEdges.equalToSuperview().inset(10) + $0.leading.equalTo(backButton.snp.trailing) + $0.trailing.equalToSuperview().inset(24) + } + } + + private func setUI() { + searchTextField.do { + $0.viewType(.search) + } + backButton.do { + $0.setImage(.leftArrowIcon, for: .normal) + $0.addAction(UIAction { [weak self] _ in + self?.dismissClosure?() + }, for: .touchUpInside) + } + } +} diff --git a/GEON-PPANG-iOS/Presentation/Search/Views/SearchResultView.swift b/GEON-PPANG-iOS/Presentation/Search/Views/SearchResultView.swift new file mode 100644 index 00000000..1f32a154 --- /dev/null +++ b/GEON-PPANG-iOS/Presentation/Search/Views/SearchResultView.swift @@ -0,0 +1,64 @@ +// +// SearchResultView.swift +// GEON-PPANG-iOS +// +// Created by JEONGEUN KIM on 2023/07/12. +// + +import UIKit + +import SnapKit +import Then + +final class SearchResultView: UIView { + + // MARK: - UI Property + + private let resultLabel = UILabel() + private let lineView = UIView() + + // MARK: - Life Cycle + + override init(frame: CGRect) { + super.init(frame: .zero) + + setLayout() + setUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Setting + + private func setLayout() { + addSubviews(resultLabel, lineView) + + resultLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(12) + $0.leading.equalToSuperview().offset(24) + } + + lineView.snp.makeConstraints { + $0.height.equalTo(1) + $0.directionalHorizontalEdges.equalToSuperview() + $0.bottom.equalToSuperview() + } + } + + private func setUI() { + resultLabel.do { + $0.basic(font: .headLine!, color: .gbbGray600!) + } + + lineView.do { + $0.makeBorder(width: 1, color: .gbbGray200!) + } + } + + func updateUI(count: Int) { + resultLabel.text = "건빵집 결과 \(count)개" + resultLabel.partColorChange(targetString: "\(count)개", textColor: .gbbPoint1!) + } +} diff --git a/GEON-PPANG-iOS/Global/Network/Model/Bakery/WriteReviewDTO.swift b/GEON-PPANG-iOS/Presentation/WriteReview/Model/WriteReviewDTO.swift similarity index 81% rename from GEON-PPANG-iOS/Global/Network/Model/Bakery/WriteReviewDTO.swift rename to GEON-PPANG-iOS/Presentation/WriteReview/Model/WriteReviewDTO.swift index 344135fa..3f44f0a3 100644 --- a/GEON-PPANG-iOS/Global/Network/Model/Bakery/WriteReviewDTO.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/Model/WriteReviewDTO.swift @@ -9,9 +9,9 @@ import Foundation struct WriteReviewDTO: Codable { let bakeryID: Int - let isLike: Bool - let keywordList: [KeywordList] - let reviewText: String + var isLike: Bool + var keywordList: [String] + var reviewText: String enum CodingKeys: String, CodingKey { case bakeryID = "bakeryId" @@ -28,6 +28,4 @@ struct KeywordList: Codable { case special = "특별한 메뉴" case zeroWaste = "제로 웨이스트" } - - let keywordName: Keyword.RawValue } diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/View/BakeryOverviewView.swift b/GEON-PPANG-iOS/Presentation/WriteReview/View/BakeryOverviewView.swift index 06a4fe30..89f3e5f1 100644 --- a/GEON-PPANG-iOS/Presentation/WriteReview/View/BakeryOverviewView.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/View/BakeryOverviewView.swift @@ -14,9 +14,7 @@ final class BakeryOverviewView: UIView { // MARK: - Property -// private let bakeryImage: UIImage // TODO: ingredient Tag 추가 -// private let regions: [String] // MARK: - UI Property @@ -77,13 +75,14 @@ final class BakeryOverviewView: UIView { private func setUI() { bakeryImageView.do { // TODO: image 추가 시 적용 + $0.backgroundColor = .gbbPoint1 $0.makeCornerRound(radius: 5) $0.contentMode = .scaleAspectFill } // TODO: ingredient Tag 추가 sampleView.do { - $0.backgroundColor = .black + $0.backgroundColor = .gbbPoint1 } } diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionView.swift b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionView.swift index 23bb5a2d..8a8715d4 100644 --- a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionView.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionView.swift @@ -37,10 +37,27 @@ final class OptionsCollectionView: UICollectionView { private func setUI() { self.do { $0.register(cell: OptionsCollectionViewCell.self) - $0.register(header: OptionsCollectionViewHeader.self) $0.isScrollEnabled = false $0.backgroundColor = .clear } } + // MARK: - Custom Method + + private func resetCellsAreSelected() { + self.indexPathsForSelectedItems?.forEach({ indexPath in + self.deselectItem(at: indexPath, animated: false) + }) + } + + func toggleIsEnabled(to isLikeSelected: Bool) { + self.isUserInteractionEnabled = isLikeSelected + self.resetCellsAreSelected() + self.visibleCells.forEach { cell in + guard let optionCell = cell as? OptionsCollectionViewCell + else { return } + optionCell.configureCell(to: isLikeSelected ? .deselected : .disabled) + } + } + } diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewCell.swift b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewCell.swift index 5ee77735..54d7dd36 100644 --- a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewCell.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewCell.swift @@ -37,22 +37,20 @@ final class OptionsCollectionViewCell: UICollectionViewCell { } private var status: CellStatus = .deselected + private var cellText: String = "" override var isSelected: Bool { - didSet { - toggleCellSelection() - } - } - var isEnabled: Bool = false { - didSet { - toggleCellInteraction() + willSet { + configureCell(to: newValue ? .selected : .deselected ) } } + var isEnabled: Bool = true + // MARK: - UI Property - let cellLabel = UILabel() + private let cellLabel = UILabel() // MARK: - Life Cycle @@ -73,47 +71,46 @@ final class OptionsCollectionViewCell: UICollectionViewCell { private func setLayout() { addSubview(cellLabel) cellLabel.snp.makeConstraints { - $0.horizontalEdges.equalToSuperview().inset(12) - $0.verticalEdges.equalToSuperview().inset(8) + $0.horizontalEdges.equalToSuperview().inset(16) + $0.verticalEdges.equalToSuperview().inset(10) $0.height.equalTo(17) } } private func setUI() { self.do { - $0.makeCornerRound(radius: 16.5) + $0.makeCornerRound(radius: 18.5) + $0.makeBorder(width: 1.5, color: cellBorderColor) } cellLabel.do { $0.text = cellText $0.font = .captionB1 + $0.textColor = cellTextColor $0.textAlignment = .center } - - configureCell(to: status) } // MARK: - Custom Method func configureCell(to status: CellStatus) { self.status = status - self.do { $0.makeBorder(width: 1.5, color: cellBorderColor) } - cellLabel.do { $0.textColor = cellTextColor } + + self.do { + $0.makeBorder(width: 1.5, color: cellBorderColor) + } + + cellLabel.do { + $0.textColor = cellTextColor + } } func configureCellText(to text: String) { - self.cellLabel.text = text - } - - func toggleCellSelection() { - self.status = isSelected ? .selected : .deselected - configureCell(to: self.status) - } - - func toggleCellInteraction() { - self.isUserInteractionEnabled.toggle() - self.status = isEnabled ? .deselected : .disabled - configureCell(to: status) + self.cellText = text + + cellLabel.do { + $0.text = text + } } } diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewFlowLayout.swift b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewFlowLayout.swift index 4f3d7aaa..74a48a07 100644 --- a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewFlowLayout.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewFlowLayout.swift @@ -12,8 +12,10 @@ class OptionsCollectionViewFlowLayout: UICollectionViewFlowLayout { override init() { super.init() self.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + self.minimumLineSpacing = 10.0 + self.sectionInset = .zero self.minimumLineSpacing = 12.0 - self.sectionInset = UIEdgeInsets(top: 18.0, left: 0.0, bottom: 0.0, right: 0.0) + self.sectionInset = .zero } required init?(coder: NSCoder) { diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewHeader.swift b/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewHeader.swift deleted file mode 100644 index 263051f2..00000000 --- a/GEON-PPANG-iOS/Presentation/WriteReview/View/OptionsCollection/OptionsCollectionViewHeader.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// OptionsCollectionViewHeader.swift -// GEON-PPANG-iOS -// -// Created by 이성민 on 2023/07/07. -// - -import UIKit - -import SnapKit -import Then - -final class OptionsCollectionViewHeader: UICollectionReusableView { - - // MARK: - Property - - var isEnabled: Bool = false { - didSet { - toggleTitleInterface() - } - } - - // MARK: - UI Property - - private let titleLabel = UILabel() - - // MARK: - Life Cycle - - override init(frame: CGRect) { - super.init(frame: frame) - - setLayout() - setUI() - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - // MARK: - Setting - - private func setLayout() { - addSubview(titleLabel) - titleLabel.snp.makeConstraints { - $0.edges.equalToSuperview() - } - } - - private func setUI() { - titleLabel.do { - $0.font = .bodyB1 - $0.textColor = .gbbGray300 - $0.textAlignment = .left - } - } - - // MARK: - Custom Method - - func configureTitle(to title: String) { - titleLabel.text = title - } - - func toggleTitleInterface() { - titleLabel.do { - $0.textColor = isEnabled ? .black : .gbbGray300 - } - } - -} diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/View/ReviewDetailTextView.swift b/GEON-PPANG-iOS/Presentation/WriteReview/View/ReviewDetailTextView.swift index 2d3caf86..c579c22a 100644 --- a/GEON-PPANG-iOS/Presentation/WriteReview/View/ReviewDetailTextView.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/View/ReviewDetailTextView.swift @@ -11,16 +11,33 @@ final class ReviewDetailTextView: UIView { // MARK: - Property - var isLike: Bool = true { - didSet { - togglePlaceholderWithIsLike() + enum TextViewStatus { + case deactivated + case activated + case error + } + + private var borderColor: UIColor { + switch status { + case .deactivated: return .gbbGray300! + case .activated: return .gbbGray500! + case .error: return .gbbError! } } - var isEnabled: Bool = false { - didSet { - toggleInteraction() - toggleUI() + private var textColor: UIColor { + switch status { + case .deactivated: return .gbbGray300! + case .activated: return .gbbGray700! + case .error: return .gbbError! + } + } + + private var status: TextViewStatus = .deactivated + + var isLike: Bool = true { + willSet { + configurePlaceholder(with: newValue) } } @@ -75,43 +92,35 @@ final class ReviewDetailTextView: UIView { $0.textColor = .gbbGray300 $0.makeCornerRound(radius: 12) $0.makeBorder(width: 1, color: .gbbGray300!) - $0.contentInset = .init(top: 20, left: 28, bottom: 16, right: 28) + $0.textContainerInset = .init(top: 20, left: 28, bottom: 16, right: 28) + $0.clipsToBounds = true } textLimitLabel.do { $0.text = "0/500" $0.font = .captionM1 - $0.textColor = .gbbGray300 + $0.textColor = .gbbGray500 } textMinimumLimitLabel.do { $0.text = "(최소 10자)" $0.font = .captionM1 - $0.textColor = .gbbGray300 + $0.textColor = .gbbGray500 } } // MARK: - Custom Method - private func toggleInteraction() { - detailTextView.isUserInteractionEnabled.toggle() - } - - private func toggleUI() { - detailTextView.do { - $0.textColor = isEnabled ? .gbbGray500 : .gbbGray300 - } - - textLimitLabel.do { - $0.textColor = isEnabled ? .gbbGray500 : .gbbGray300 - } + func configureTextView(to status: TextViewStatus) { + self.status = status - textMinimumLimitLabel.do { - $0.textColor = isEnabled ? .gbbGray500 : .gbbGray300 + detailTextView.do { + $0.textColor = textColor + $0.makeBorder(width: 1, color: borderColor) } } - private func togglePlaceholderWithIsLike() { + func configurePlaceholder(with isLike: Bool) { detailTextView.do { $0.text = isLike ? I18N.WriteReview.likePlaceholder : I18N.WriteReview.dislikePlaceholder } @@ -123,4 +132,10 @@ final class ReviewDetailTextView: UIView { } } + func checkTextCount() { + if detailTextView.text.count < 10 { + configureTextView(to: .error) + } + } + } diff --git a/GEON-PPANG-iOS/Presentation/WriteReview/ViewController/WriteReviewViewController.swift b/GEON-PPANG-iOS/Presentation/WriteReview/ViewController/WriteReviewViewController.swift index 0e7efe57..adc2cefb 100644 --- a/GEON-PPANG-iOS/Presentation/WriteReview/ViewController/WriteReviewViewController.swift +++ b/GEON-PPANG-iOS/Presentation/WriteReview/ViewController/WriteReviewViewController.swift @@ -16,29 +16,41 @@ final class WriteReviewViewController: BaseViewController { private var likeCollectionViewHeightConstraint: NSLayoutConstraint! + private let keywordList = KeywordList.Keyword.allCases.map { $0.rawValue } + private var writeReviewData: WriteReviewDTO = .init(bakeryID: 1, isLike: false, keywordList: [], reviewText: "") + // MARK: - UI Property - // TODO: navigationBar 추가 // TODO: bakeryImage 추가 private let scrollView = UIScrollView() private let contentView = UIView() + + private let navigationBar = CustomNavigationBar() private let bakeryOverviewView = BakeryOverviewView(bakeryImage: .actions, firstRegion: "tset", secondRegion: "efqerqf") private let lineView = LineView() + private let likeCollectionViewFlowLayout = OptionsCollectionViewFlowLayout() private let optionsCollectionViewFlowLayout = OptionsCollectionViewFlowLayout() + private let likeCollectionViewHeaderLabel = UILabel() private lazy var likeCollectionView = OptionsCollectionView(frame: .zero, collectionViewLayout: likeCollectionViewFlowLayout) + private let optionsCollectionViewHeaderLabel = UILabel() private lazy var optionsCollectionView = OptionsCollectionView(frame: .zero, collectionViewLayout: optionsCollectionViewFlowLayout) private let reviewDetailTextView = ReviewDetailTextView() + private let dotView = UILabel() + private let aboutReviewContainerView = UIView() private let aboutReviewLabel = UILabel() + private let bottomView = BottomView() + private let nextButton = CommonButton() + // MARK: - life cycle override func viewDidLoad() { super.viewDidLoad() - // TODO: baseVC 에 숨기기 - self.navigationController?.navigationBar.isHidden = true + setNavigationBarHidden() setKeyboardHideGesture() + setKeyboardNotificationCenterOnScrollView() } override func viewWillLayoutSubviews() { @@ -53,8 +65,17 @@ final class WriteReviewViewController: BaseViewController { override func setLayout() { view.addSubview(scrollView) scrollView.snp.makeConstraints { - $0.verticalEdges.equalToSuperview() - $0.horizontalEdges.equalToSuperview() + $0.edges.equalToSuperview() + } + + view.addSubview(navigationBar) + navigationBar.snp.makeConstraints { + $0.horizontalEdges.top.equalToSuperview() + } + + view.addSubview(bottomView) + bottomView.snp.makeConstraints { + $0.horizontalEdges.bottom.equalToSuperview() } scrollView.addSubview(contentView) @@ -64,8 +85,8 @@ final class WriteReviewViewController: BaseViewController { contentView.addSubview(bakeryOverviewView) bakeryOverviewView.snp.makeConstraints { + $0.top.equalToSuperview().inset(68) $0.horizontalEdges.equalToSuperview() - $0.top.equalToSuperview() $0.height.equalTo(125) } @@ -76,16 +97,30 @@ final class WriteReviewViewController: BaseViewController { $0.height.equalTo(1) } + contentView.addSubview(likeCollectionViewHeaderLabel) + likeCollectionViewHeaderLabel.snp.makeConstraints { + $0.top.equalTo(lineView.snp.bottom).offset(24) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.height.equalTo(22) + } + contentView.addSubview(likeCollectionView) likeCollectionView.snp.makeConstraints { - $0.top.equalTo(lineView.snp.bottom).offset(24) + $0.top.equalTo(likeCollectionViewHeaderLabel.snp.bottom).offset(18) $0.horizontalEdges.equalToSuperview().inset(24) $0.height.equalTo(73) } + contentView.addSubview(optionsCollectionViewHeaderLabel) + optionsCollectionViewHeaderLabel.snp.makeConstraints { + $0.top.equalTo(likeCollectionView.snp.bottom).offset(28) + $0.horizontalEdges.equalToSuperview().inset(24) + $0.height.equalTo(22) + } + contentView.addSubview(optionsCollectionView) optionsCollectionView.snp.makeConstraints { - $0.top.equalTo(likeCollectionView.snp.bottom).offset(28) + $0.top.equalTo(optionsCollectionViewHeaderLabel.snp.bottom).offset(18) $0.horizontalEdges.equalToSuperview().inset(24) $0.height.equalTo(50) } @@ -97,15 +132,45 @@ final class WriteReviewViewController: BaseViewController { $0.height.equalTo(221) } - contentView.addSubview(aboutReviewLabel) - aboutReviewLabel.snp.makeConstraints { - $0.horizontalEdges.equalToSuperview().inset(24) - $0.top.equalTo(reviewDetailTextView.snp.bottom).offset(16) + contentView.addSubview(aboutReviewContainerView) + aboutReviewContainerView.snp.makeConstraints { + $0.top.equalTo(reviewDetailTextView.snp.bottom).offset(10) + $0.horizontalEdges.equalToSuperview() + $0.height.equalTo(207) $0.bottom.equalToSuperview() } + + aboutReviewContainerView.addSubview(dotView) + dotView.snp.makeConstraints { + $0.leading.equalToSuperview().inset(24) + $0.top.equalToSuperview().offset(16) + } + + aboutReviewContainerView.addSubview(aboutReviewLabel) + aboutReviewLabel.snp.makeConstraints { + $0.leading.equalTo(dotView.snp.trailing) + $0.trailing.equalToSuperview().inset(24) + $0.top.equalToSuperview().offset(16) + } } override func setUI() { + navigationBar.do { + $0.backgroundColor = .white + $0.configureLeftTitle(to: "건대 초코빵") + $0.configureBottomLine() + } + + scrollView.do { + $0.showsVerticalScrollIndicator = false + $0.verticalScrollIndicatorInsets = .zero + $0.backgroundColor = .gbbGray100 + } + + contentView.do { + $0.backgroundColor = .white + } + likeCollectionViewFlowLayout.do { $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize } @@ -114,20 +179,68 @@ final class WriteReviewViewController: BaseViewController { $0.estimatedItemSize = UICollectionViewFlowLayout.automaticSize } + likeCollectionViewHeaderLabel.do { + $0.text = I18N.WriteReview.likeOptionTitle + $0.font = .bodyB1 + $0.textColor = .black + } + + optionsCollectionViewHeaderLabel.do { + $0.text = I18N.WriteReview.optionTitle + $0.font = .bodyB1 + $0.textColor = .gbbGray300 + } + optionsCollectionView.do { $0.allowsMultipleSelection = true + $0.isUserInteractionEnabled = false + } + + reviewDetailTextView.do { + $0.isUserInteractionEnabled = false + } + + aboutReviewContainerView.do { + $0.backgroundColor = .gbbGray100 + } + + dotView.do { + $0.font = .captionM2 + $0.textColor = .gbbGray300 + $0.textAlignment = .center + $0.setLineHeight(by: 1.37, with: "•") } aboutReviewLabel.do { $0.text = I18N.WriteReview.aboutReview $0.font = .captionM2 - $0.textColor = .gbbGray400 + $0.textColor = .gbbGray300 $0.textAlignment = .left $0.numberOfLines = 4 + $0.setLineHeight(by: 1.37, with: I18N.WriteReview.aboutReview) + } + + bottomView.do { + $0.backgroundColor = .white + $0.layer.masksToBounds = false + $0.applyAdditionalSubview(nextButton, withTrailingOffset: 16) + } + + nextButton.do { + $0.backgroundColor = .gbbPoint1 + $0.isUserInteractionEnabled = false + $0.makeCornerRound(radius: 12) + $0.getButtonTitle(.write) + $0.getButtonUI(.gbbGray200!) + $0.addAction(UIAction { [weak self] _ in + self?.nextButtonTapped() + }, for: .touchUpInside) } } override func setDelegate() { + scrollView.delegate = self + likeCollectionView.delegate = self likeCollectionView.dataSource = self @@ -137,6 +250,18 @@ final class WriteReviewViewController: BaseViewController { reviewDetailTextView.detailTextView.delegate = self } + private func setKeyboardNotificationCenterOnScrollView() { + NotificationCenter.default.addObserver(self, selector: #selector(moveUpAboutKeyboardOnScrollView), name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(moveDownAboutKeyboardOnScrollView), name: UIResponder.keyboardWillHideNotification, object: nil) + } + + // MARK: - Action Helper + + private func nextButtonTapped() { + writeReviewData.reviewText = reviewDetailTextView.detailTextView.text + dump(writeReviewData) + } + // MARK: - Custom Method private func updateCollectionViewConstraint(of collectionView: UICollectionView) { @@ -147,11 +272,100 @@ final class WriteReviewViewController: BaseViewController { } } + private func configureCollectionViewHeader(to color: UIColor) { + optionsCollectionViewHeaderLabel.do { + $0.textColor = color + } + } + + private func checkTextViewLength(_ textView: UITextView) { + if textView.text.count <= 10 { + reviewDetailTextView.configureTextView(to: .error) + } else { + reviewDetailTextView.configureTextView(to: .activated) + } + } + + private func textLimit(_ existingText: String?, to newText: String, with limit: Int) -> Bool { + guard let text = existingText + else { return false } + let isOverLimit = text.count + newText.count <= limit + return isOverLimit + } + + // MARK: - objc + + @objc + func moveUpAboutKeyboardOnScrollView(_ notification: NSNotification) { + if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { + UIView.animate(withDuration: 0.2, animations: { + self.scrollView.transform = CGAffineTransform(translationX: 0, y: -keyboardSize.height + 24) + self.bottomView.transform = CGAffineTransform(translationX: 0, y: -keyboardSize.height + 40) + }) + } + } + + @objc + func moveDownAboutKeyboardOnScrollView(_ notification: NSNotification) { + UIView.animate(withDuration: 0.2, animations: { + self.scrollView.transform = .identity + self.bottomView.transform = .identity + self.nextButton.transform = .identity + }) + } + } // MARK: - UICollectionViewDelegate extension -extension WriteReviewViewController: UICollectionViewDelegate {} +extension WriteReviewViewController: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + switch collectionView { + case likeCollectionView: + guard let isLikeSelected = collectionView.cellForItem(at: [0, 0])?.isSelected + else { return } + + optionsCollectionView.toggleIsEnabled(to: isLikeSelected) + if !isLikeSelected { + writeReviewData.keywordList.removeAll() + } + + configureCollectionViewHeader(to: isLikeSelected ? .black : .gbbGray300!) + + reviewDetailTextView.isLike = isLikeSelected + reviewDetailTextView.isUserInteractionEnabled = !isLikeSelected + reviewDetailTextView.configureTextView(to: isLikeSelected ? .deactivated : .activated) + + writeReviewData.isLike = isLikeSelected + + case optionsCollectionView: + let hasSelection = collectionView.indexPathsForSelectedItems != nil + reviewDetailTextView.isUserInteractionEnabled = hasSelection + reviewDetailTextView.configureTextView(to: hasSelection ? .activated : .deactivated) + reviewDetailTextView.checkTextCount() + + writeReviewData.keywordList.append(keywordList[indexPath.item]) + + default: + return + } + } + + func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + guard collectionView == optionsCollectionView else { return } + + if let keywordIndex = writeReviewData.keywordList.firstIndex(of: keywordList[indexPath.item]) { + writeReviewData.keywordList.remove(at: keywordIndex) + } + + if collectionView.indexPathsForSelectedItems == [] { + reviewDetailTextView.isUserInteractionEnabled = false + reviewDetailTextView.configureTextView(to: .deactivated) + } + } + +} // MARK: - UICollectionViewDataSource extension @@ -166,7 +380,6 @@ extension WriteReviewViewController: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell: OptionsCollectionViewCell = collectionView.dequeueReusableCell(for: indexPath) switch collectionView { @@ -174,45 +387,41 @@ extension WriteReviewViewController: UICollectionViewDataSource { cell.configureCell(to: .deselected) cell.configureCellText(to: indexPath.item == 0 ? I18N.WriteReview.like : I18N.WriteReview.dislike) case optionsCollectionView: - let keywordList = KeywordList.Keyword.allCases.map { $0.rawValue } cell.configureCell(to: .disabled) cell.configureCellText(to: keywordList[indexPath.item]) - cell.isUserInteractionEnabled = false - default: return UICollectionViewCell() + default: + return UICollectionViewCell() } return cell } - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - let header: OptionsCollectionViewHeader = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, indexPath: indexPath) - - switch collectionView { - case likeCollectionView: - header.configureTitle(to: I18N.WriteReview.likeOptionTitle) - header.isEnabled.toggle() - case optionsCollectionView: - header.configureTitle(to: I18N.WriteReview.optionTitle) - default: - return UICollectionReusableView() - } - return header + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 18, left: 0, bottom: 0, right: 0) } } -// MARK: - UICollectionViewDelegateFlowLayout - -extension WriteReviewViewController: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - return .init(width: collectionView.frame.width, height: 22) - } -} - // MARK: - UITextViewDelegate extension WriteReviewViewController: UITextViewDelegate { + + func textViewDidChange(_ textView: UITextView) { + let textCount = textView.text.count + if textCount < 10 && 0 < textCount { + reviewDetailTextView.configureTextView(to: .error) + + nextButton.getButtonUI(.gbbGray200!) + nextButton.isUserInteractionEnabled = false + } else { + reviewDetailTextView.configureTextView(to: .activated) + + nextButton.getButtonUI(.gbbGray700!) + nextButton.isUserInteractionEnabled = true + } + reviewDetailTextView.updateTextLimitLabel(to: textCount) + } + func textViewDidBeginEditing(_ textView: UITextView) { - let textView = self.reviewDetailTextView.detailTextView if textView.text == I18N.WriteReview.likePlaceholder || textView.text == I18N.WriteReview.dislikePlaceholder { textView.text = nil textView.textColor = .black @@ -220,10 +429,14 @@ extension WriteReviewViewController: UITextViewDelegate { } func textViewDidEndEditing(_ textView: UITextView) { - let textView = self.reviewDetailTextView.detailTextView if textView.text.isEmpty { textView.text = reviewDetailTextView.isLike ? I18N.WriteReview.likePlaceholder : I18N.WriteReview.dislikePlaceholder textView.textColor = .gbbGray300 } } + + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + return self.textLimit(textView.text, to: text, with: 70) + } + } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/Contents.json similarity index 66% rename from GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/Contents.json rename to GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/Contents.json index b96c65ab..4ed22a14 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "img_no_myreview@1x.png", + "filename" : "ic_launchscreen@1x.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_no_myreview@2x.png", + "filename" : "ic_launchscreen@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_no_myreview@3x.png", + "filename" : "ic_launchscreen@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@1x.png new file mode 100644 index 00000000..f66f6c38 Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@1x.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@2x.png new file mode 100644 index 00000000..24220bd0 Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@2x.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@3x.png new file mode 100644 index 00000000..fc55faec Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_launchscreen.imageset/ic_launchscreen@3x.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/Contents.json similarity index 70% rename from GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/Contents.json rename to GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/Contents.json index 93c2f913..9480cc23 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "ic_title@1x.png", + "filename" : "ic_profile.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "ic_title@2x.png", + "filename" : "ic_profile 1.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "ic_title@3x.png", + "filename" : "ic_profile 2.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile 1.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile 1.png new file mode 100644 index 00000000..282f1c8e Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile 1.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile 2.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile 2.png new file mode 100644 index 00000000..3acadb23 Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile 2.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile.png new file mode 100644 index 00000000..c8f1d3de Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/icons/ic_profile.imageset/ic_profile.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/Contents.json index 3d0a8af5..9fecf89f 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "img_no_bookmark@1x.png", + "filename" : "img_no_bookmark.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_no_bookmark@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_no_bookmark@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark.svg b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark.svg new file mode 100644 index 00000000..c0193287 --- /dev/null +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@1x.png deleted file mode 100644 index 24ee0839..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@2x.png deleted file mode 100644 index 37fd3a6a..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@3x.png deleted file mode 100644 index 4d9770dc..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_bookmark.imageset/img_no_bookmark@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@1x.png deleted file mode 100644 index 84342b8b..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@2x.png deleted file mode 100644 index 11510278..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@3x.png deleted file mode 100644 index 282e6579..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_myreview.imageset/img_no_myreview@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/Contents.json index ef7a2d3f..5eef1317 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "img_no_review@1x.png", + "filename" : "img_no_review.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_no_review@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_no_review@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review.svg b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review.svg new file mode 100644 index 00000000..8b910959 --- /dev/null +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@1x.png deleted file mode 100644 index 5db2cb60..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@2x.png deleted file mode 100644 index 0e627de6..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@3x.png deleted file mode 100644 index ca594fba..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_review.imageset/img_no_review@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/Contents.json index b943180a..77772d8b 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "img_no_search_result@1x.png", + "filename" : "img_no_search_result.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_no_search_result@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_no_search_result@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result.svg b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result.svg new file mode 100644 index 00000000..bdc1499d --- /dev/null +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@1x.png deleted file mode 100644 index ef16cdf7..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@2x.png deleted file mode 100644 index e3e1ecff..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@3x.png deleted file mode 100644 index 5eca8263..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_no_search_result.imageset/img_no_search_result@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/Contents.json index de222f00..0b5586a5 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "img_search@1x.png", + "filename" : "img_search.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_search@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_search@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search.svg b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search.svg new file mode 100644 index 00000000..80c697e6 --- /dev/null +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@1x.png deleted file mode 100644 index a147d439..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@2x.png deleted file mode 100644 index a26c3663..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@3x.png deleted file mode 100644 index bd3002d5..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_search.imageset/img_search@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/Contents.json index 57eac8a2..43e43fa3 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "img_server_maintenance@1x.png", + "filename" : "img_server_maintenance.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_server_maintenance@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_server_maintenance@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance.svg b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance.svg new file mode 100644 index 00000000..1006e586 --- /dev/null +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@1x.png deleted file mode 100644 index b0669261..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@2x.png deleted file mode 100644 index 08bbd7e7..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@3x.png deleted file mode 100644 index 90cd7e65..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_server_maintenance.imageset/img_server_maintenance@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/Contents.json index b271cd8e..b6073907 100644 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/Contents.json +++ b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "img_welcome@1x.png", + "filename" : "img_welcome.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "img_welcome@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "img_welcome@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome.png new file mode 100644 index 00000000..8ef08791 Binary files /dev/null and b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome.png differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@1x.png deleted file mode 100644 index b916b028..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@2x.png deleted file mode 100644 index 7981ffdd..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@3x.png deleted file mode 100644 index 9f56775b..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/images/img_welcome.imageset/img_welcome@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/Contents.json b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/Contents.json deleted file mode 100644 index 64226422..00000000 --- a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "ic_splash_image@1x.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "ic_splash_image@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "ic_splash_image@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@1x.png deleted file mode 100644 index ce2b8321..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@2x.png deleted file mode 100644 index af27a082..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@3x.png deleted file mode 100644 index b6b38b6c..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_splash_image.imageset/ic_splash_image@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@1x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@1x.png deleted file mode 100644 index 597ca208..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@1x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@2x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@2x.png deleted file mode 100644 index 3647fcb0..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@2x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@3x.png b/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@3x.png deleted file mode 100644 index 3a5941dc..00000000 Binary files a/GEON-PPANG-iOS/Resource/Assets/Assets.xcassets/splash/ic_title.imageset/ic_title@3x.png and /dev/null differ diff --git a/GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/gray-800.colorset/Contents.json b/GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/black.colorset/Contents.json similarity index 100% rename from GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/gray-800.colorset/Contents.json rename to GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/black.colorset/Contents.json diff --git a/GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/gray-50.colorset/Contents.json b/GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/white.colorset/Contents.json similarity index 100% rename from GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/gray-50.colorset/Contents.json rename to GEON-PPANG-iOS/Resource/Colors/Colors.xcassets/Gray_Scale/white.colorset/Contents.json diff --git a/GEON-PPANG-iOS/Resource/Storyboard/Base.lproj/LaunchScreen.storyboard b/GEON-PPANG-iOS/Resource/Storyboard/Base.lproj/LaunchScreen.storyboard index 865e9329..015b1476 100644 --- a/GEON-PPANG-iOS/Resource/Storyboard/Base.lproj/LaunchScreen.storyboard +++ b/GEON-PPANG-iOS/Resource/Storyboard/Base.lproj/LaunchScreen.storyboard @@ -1,7 +1,10 @@ - - + + + - + + + @@ -11,15 +14,34 @@ - + - + + + + + + + + + + + + + + - + + + + + + +