Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

편의점 바텀시트 구현 #49

Merged
merged 5 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Entity/Sources/Entity/ConvenienceStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation
import SwiftUI

public enum ConvenienceStore: String, Codable {
public enum ConvenienceStore: String, Codable, CaseIterable {
case cu = "CU"
case gs25 = "GS25"
case _7Eleven = "7-ElEVEN"
Expand Down
12 changes: 12 additions & 0 deletions PyeonHaeng-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
BA28F1A42B61572A0052855E /* Pretendard-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = BA28F19A2B61572A0052855E /* Pretendard-ExtraBold.otf */; };
BA28F1A52B61572A0052855E /* Pretendard-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = BA28F19B2B61572A0052855E /* Pretendard-Light.otf */; };
BA28F1A62B61572A0052855E /* Pretendard-Black.otf in Resources */ = {isa = PBXBuildFile; fileRef = BA28F19C2B61572A0052855E /* Pretendard-Black.otf */; };
BA402F7E2B85E31800E86AAD /* ConvenienceSelectBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA402F7D2B85E31800E86AAD /* ConvenienceSelectBottomSheetView.swift */; };
BA4EA3602B6A37E10003DCE7 /* Entity in Frameworks */ = {isa = PBXBuildFile; productRef = BA4EA35F2B6A37E10003DCE7 /* Entity */; };
BAA4D9AD2B5A1795005999F8 /* PyeonHaengApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA4D9AC2B5A1795005999F8 /* PyeonHaengApp.swift */; };
BAA4D9AF2B5A1795005999F8 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAA4D9AE2B5A1795005999F8 /* SplashView.swift */; };
Expand Down Expand Up @@ -87,6 +88,7 @@
BA28F19B2B61572A0052855E /* Pretendard-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Light.otf"; sourceTree = "<group>"; };
BA28F19C2B61572A0052855E /* Pretendard-Black.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Black.otf"; sourceTree = "<group>"; };
BA28F1A72B6157E90052855E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BA402F7D2B85E31800E86AAD /* ConvenienceSelectBottomSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvenienceSelectBottomSheetView.swift; sourceTree = "<group>"; };
BA4EA35A2B6A00F70003DCE7 /* Entity */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Entity; sourceTree = "<group>"; };
BAA4D9A92B5A1795005999F8 /* PyeonHaeng-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PyeonHaeng-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
BAA4D9AC2B5A1795005999F8 /* PyeonHaengApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PyeonHaengApp.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -218,6 +220,7 @@
BA28F1862B61558C0052855E /* HomeScene */ = {
isa = PBXGroup;
children = (
BA402F7C2B85E2DE00E86AAD /* BottomSheet */,
BA28F1872B6155910052855E /* HomeView.swift */,
BAB5CF262B6B7CF3008B24BF /* HomeViewModel.swift */,
BAE159D72B65FA6F002DCF94 /* HomeProductDetailSelectionView.swift */,
Expand Down Expand Up @@ -271,6 +274,14 @@
path = Fonts;
sourceTree = "<group>";
};
BA402F7C2B85E2DE00E86AAD /* BottomSheet */ = {
isa = PBXGroup;
children = (
BA402F7D2B85E31800E86AAD /* ConvenienceSelectBottomSheetView.swift */,
);
path = BottomSheet;
sourceTree = "<group>";
};
BAA4D9A02B5A1795005999F8 = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -505,6 +516,7 @@
files = (
BAE159DA2B65FC35002DCF94 /* HomeProductListView.swift in Sources */,
E5F2EC402B637D4A00EE0838 /* ProductInfoHeader.swift in Sources */,
BA402F7E2B85E31800E86AAD /* ConvenienceSelectBottomSheetView.swift in Sources */,
BA28F1852B6155810052855E /* OnboardingView.swift in Sources */,
BAB5CF272B6B7CF3008B24BF /* HomeViewModel.swift in Sources */,
BA28F1882B6155910052855E /* HomeView.swift in Sources */,
Expand Down
120 changes: 111 additions & 9 deletions PyeonHaeng-iOS/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,86 @@
}
}
},
"7-Eleven" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "7-Eleven"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "세븐일레븐"
}
}
}
},
"CU" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "CU"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "씨유"
}
}
}
},
"Emart 24" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "Emart 24"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "이마트 24"
}
}
}
},
"GS25" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "GS25"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "지에스 25"
}
}
}
},
"Ministop" : {
"localizations" : {
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ministop"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "미니스톱"
}
}
}
},
"개당" : {
"extractionState" : "manual",
"localizations" : {
Expand Down Expand Up @@ -306,47 +386,47 @@
}
}
},
"이전 행사 정보" : {
"오름차순 정렬" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Previous Promotions"
"value" : "Ascending Sorting"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "過去のプロモーション"
"value" : "昇順並び替え"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "이전 행사 정보"
"value" : "오름차순 정렬"
}
}
}
},
"오름차순 정렬" : {
"extractionState" : "manual",
"이전 행사 정보" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ascending Sorting"
"value" : "Previous Promotions"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "昇順並び替え"
"value" : "過去のプロモーション"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "오름차순 정렬"
"value" : "이전 행사 정보"
}
}
}
Expand Down Expand Up @@ -420,6 +500,28 @@
}
}
},
"편의점 브랜드 선택" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Choose Store Brand"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "コンビニのブランドを選ぶ"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "편의점 브랜드 선택"
}
}
}
},
"행사 진행 편의점" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// ConvenienceSelectBottomSheetView.swift
// PyeonHaeng-iOS
//
// Created by 홍승현 on 2/21/24.
//

import Entity
import SwiftUI

// MARK: - ConvenienceSelectBottomSheetView

struct ConvenienceSelectBottomSheetView<ViewModel>: View where ViewModel: HomeViewModelRepresentable {
@EnvironmentObject private var viewModel: ViewModel
@Environment(\.dismiss) private var dismiss

var body: some View {
VStack(spacing: Metrics.itemSpacing) {
Text("편의점 브랜드 선택")
.font(.h3)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, Metrics.horizontalPadding)

ForEach(ConvenienceStore.allCases, id: \.self) { store in
Button {
viewModel.trigger(.changeConvenienceStore(store))
dismiss()
} label: {
ConvenienceSelectItem(convenience: store)
.frame(maxWidth: .infinity, minHeight: Metrics.itemHeight, alignment: .leading)
}
}
.padding(.horizontal, Metrics.itemHorizontalPadding)
}
.padding(.top, Metrics.topPadding)
.padding(.bottom, Metrics.bottomPadding)
}
}

// MARK: - ConvenienceSelectItem

private struct ConvenienceSelectItem: View {
private let convenience: ConvenienceStore

init(convenience: ConvenienceStore) {
self.convenience = convenience
}

var body: some View {
HStack(spacing: Metrics.itemHorizontalSpacing) {
convenienceImageView()
convenienceText()
}
}

private func convenienceImageView() -> Image {
switch convenience {
case .cu:
.cu
case .gs25:
.gs25
case ._7Eleven:
._7Eleven
case .emart24:
.emart24
case .ministop:
.ministop
}
}

private func convenienceText() -> Text {
switch convenience {
case .cu:
Text("CU")
case .gs25:
Text("GS25")
case ._7Eleven:
Text("7-Eleven")
case .emart24:
Text("Emart 24")
case .ministop:
Text("Ministop")
}
}
}

// MARK: - Metrics

private enum Metrics {
static let topPadding: CGFloat = 12
static let bottomPadding: CGFloat = 4
static let horizontalPadding: CGFloat = 20

// MARK: Item

static let itemHorizontalPadding: CGFloat = 24
static let itemHorizontalSpacing: CGFloat = 12
static let itemSpacing: CGFloat = 4
static let itemHeight: CGFloat = 44
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import SwiftUI

// MARK: - HomeProductDetailSelectionView

struct HomeProductDetailSelectionView: View {
struct HomeProductDetailSelectionView<ViewModel>: View where ViewModel: HomeViewModelRepresentable {
@EnvironmentObject private var viewModel: ViewModel
@State private var convenienceStoreModalPresented: Bool = false

var body: some View {
HStack {
Button {} label: {
Button {
convenienceStoreModalPresented = true
} label: {
Group {
Image.gs25
.resizable()
Expand All @@ -25,6 +30,12 @@ struct HomeProductDetailSelectionView: View {
}
}
.accessibilityHint("더블 탭하여 편의점을 선택하세요")
.sheet(isPresented: $convenienceStoreModalPresented) {
ConvenienceSelectBottomSheetView<ViewModel>()
.presentationDetents([.height(Metrics.bottomSheetHeight)])
.presentationCornerRadius(20)
.presentationBackground(.regularMaterial)
}

Spacer()

Expand Down Expand Up @@ -57,22 +68,21 @@ struct HomeProductDetailSelectionView: View {
}
}

// MARK: HomeProductDetailSelectionView.Metrics
// MARK: - Metrics

private extension HomeProductDetailSelectionView {
enum Metrics {
static let buttonSpacing: CGFloat = 2
static let textHeight: CGFloat = 24
static let horizontal: CGFloat = 20
static let iconWidth: CGFloat = 8
static let iconHeight: CGFloat = 4
private enum Metrics {
static let buttonSpacing: CGFloat = 2
static let textHeight: CGFloat = 24
static let horizontal: CGFloat = 20
static let iconWidth: CGFloat = 8
static let iconHeight: CGFloat = 4

static let promotionButtonPaddingTop: CGFloat = 4
static let promotionButtonPaddingLeading: CGFloat = 16
static let promotionButtonPaddingBottom: CGFloat = 4
static let promotionButtonPaddingTrailing: CGFloat = 10
static let promotionButtonCornerRadius: CGFloat = 16
static let promotionButtonPaddingTop: CGFloat = 4
static let promotionButtonPaddingLeading: CGFloat = 16
static let promotionButtonPaddingBottom: CGFloat = 4
static let promotionButtonPaddingTrailing: CGFloat = 10
static let promotionButtonCornerRadius: CGFloat = 16

static let height: CGFloat = 56
}
static let height: CGFloat = 56
static let bottomSheetHeight: CGFloat = 334
}
Loading