From cfac65803f5ecca536ce9de3ec94827b606ad5df Mon Sep 17 00:00:00 2001 From: Yeji Date: Wed, 15 Jan 2025 17:57:58 +0900 Subject: [PATCH 1/6] [feat] #26 feat signUpTab Content --- .../Presentation/SignUp/.gitkeep | 0 .../Presentation/SignUp/SignUpContent.swift | 41 +++++++++ .../SignUp/SignUpContentCase.swift | 37 ++++++++ .../Presentation/SignUp/SignUpFeature.swift | 86 +++++++++++++++++++ .../Presentation/SignUp/SignUpView.swift | 37 ++++++++ .../SignUp/View/ActiveAreaView.swift | 18 ++++ .../SignUp/View/PhoneAuthenticationView.swift | 18 ++++ .../SignUp/View/ProfileImageView.swift | 18 ++++ .../SignUp/View/SelectBirthYearView.swift | 18 ++++ .../SignUp/View/SelectGenderView.swift | 19 ++++ .../SignUp/View/TermsOfServiceView.swift | 18 ++++ .../SignUp/View/WriteNickNameView.swift | 18 ++++ 12 files changed, 328 insertions(+) delete mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/.gitkeep create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContentCase.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ActiveAreaView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/PhoneAuthenticationView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ProfileImageView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectBirthYearView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectGenderView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/WriteNickNameView.swift diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/.gitkeep b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift new file mode 100644 index 0000000..a48f763 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift @@ -0,0 +1,41 @@ +// +// SignUpContent.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct SignUpContent: View { + @Binding var selectedTab: SignUpContentCase + init(selectedTab: Binding) { + self._selectedTab = selectedTab + UITabBar.appearance().isHidden = true + } + + var body: some View { + HStack(spacing: 0) { + TabView(selection: $selectedTab) { + TermsOfServiceView() + .tag(SignUpContentCase.termsOfServiceView) + PhoneAuthenticationView() + .tag(SignUpContentCase.authenticationView) + WriteNickNameView() + .tag(SignUpContentCase.nickNameView) + SelectBirthYearView() + .tag(SignUpContentCase.birthYearView) + SelectGenderView() + .tag(SignUpContentCase.genderView) + ProfileImageView() + .tag(SignUpContentCase.profileImageView) + ActiveAreaView() + .tag(SignUpContentCase.activeAreaView) + } + .tabViewStyle(PageTabViewStyle()) + .onAppear { + UIScrollView.appearance().isScrollEnabled = false + } + } + } +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContentCase.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContentCase.swift new file mode 100644 index 0000000..0973575 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContentCase.swift @@ -0,0 +1,37 @@ +// +// SignUpContentCase.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import Foundation + +public enum SignUpContentCase: CaseIterable { + case termsOfServiceView + case authenticationView + case nickNameView + case birthYearView + case genderView + case profileImageView + case activeAreaView + + var title: String { + switch self { + case .termsOfServiceView: + return "수현이랑\n서비스 이용약관" + case .authenticationView: + return "본인인증을 위한\n휴대폰 번호 인증이 필요해요" + case .nickNameView: + return "수현이랑에서 사용할\n닉네임을 입력해주세요" + case .birthYearView: + return "태어난 년도를\n선택해주세요" + case .genderView: + return "성별을\n선택해주세요" + case .profileImageView: + return "프로필 이미지를\n등록해주세요" + case .activeAreaView: + return "자주 활동하는\n지역을 선택해주세요" + } + } +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift new file mode 100644 index 0000000..5bba732 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift @@ -0,0 +1,86 @@ +// +// SignUpFeature.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import Foundation +import Combine + +class SignUpFeature: Feature { + struct State { + var progress: Double = 0.0 + var isAgree: Bool = false // 약관 동의 + + } + + enum Intent { + case nextStep + case previousStep + } + + enum SideEffect { + + } + + @Published private(set) var state = State() + @Published var currentContent: SignUpContentCase = .termsOfServiceView + private var cancellables = Set() + private let intentSubject = PassthroughSubject() + let sideEffectSubject = PassthroughSubject() + + init() { + bindIntents() + updateProgress() + } + + private func bindIntents() { + intentSubject.sink { [weak self] intent in + self?.handleIntent(intent) + }.store(in: &cancellables) + } + + func send(_ intent: Intent) { + intentSubject.send(intent) + } + + func handleIntent(_ intent: Intent) { + switch intent { + case .nextStep: + moveToNextStep() + case .previousStep: + moveToPreviousStep() + } + } + + private func moveToNextStep() { + if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent), + currentIndex < SignUpContentCase.allCases.count - 1 { + print(currentIndex, "-" , currentContent) + currentContent = SignUpContentCase.allCases[currentIndex + 1] + updateProgress() + } + } + + private func moveToPreviousStep() { + if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent), + currentIndex > 0 { + currentContent = SignUpContentCase.allCases[currentIndex - 1] + updateProgress() + } + } + + private func updateProgress() { + if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent) { + state.progress = Double(currentIndex + 1) / Double(SignUpContentCase.allCases.count) * 100 + } + } + + + func changeSelectedContent(signUpContentCase: SignUpContentCase) { + currentContent = signUpContentCase + } +} + + diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift new file mode 100644 index 0000000..032bdc1 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift @@ -0,0 +1,37 @@ +// +// SignUpView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct SignUpView: View { + @EnvironmentObject private var router: RouterRegistry + @StateObject private var signUpFeature = SignUpFeature() + + var body: some View { + VStack(alignment: .leading) { + WithSuhyeonTopNavigationBar(title: "", onTapLeft: {}) + + WithSuhyeonProgressBar(progress: signUpFeature.state.progress) + + Text(signUpFeature.currentContent.title) + .font(.title02B) + .padding(.leading, 16) + .padding(.vertical, 20) + + SignUpContent(selectedTab: $signUpFeature.currentContent) + + WithSuhyeonButton(title: "버튼", buttonState: .disabled, onTapButton: { + signUpFeature.send(.nextStep) + }) + .padding(.horizontal, 16) + } + } +} + +#Preview { + SignUpView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ActiveAreaView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ActiveAreaView.swift new file mode 100644 index 0000000..9900e11 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ActiveAreaView.swift @@ -0,0 +1,18 @@ +// +// ActiveAreaView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct ActiveAreaView: View { + var body: some View { + Text("활동 지역 선택") + } +} + +#Preview { + ActiveAreaView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/PhoneAuthenticationView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/PhoneAuthenticationView.swift new file mode 100644 index 0000000..baa74b7 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/PhoneAuthenticationView.swift @@ -0,0 +1,18 @@ +// +// PhoneAuthenticationView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct PhoneAuthenticationView: View { + var body: some View { + Text("핸드폰 인증") + } +} + +#Preview { + PhoneAuthenticationView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ProfileImageView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ProfileImageView.swift new file mode 100644 index 0000000..5640a1b --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/ProfileImageView.swift @@ -0,0 +1,18 @@ +// +// ProfileImageView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct ProfileImageView: View { + var body: some View { + Text("프로필 이미지 선택") + } +} + +#Preview { + ProfileImageView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectBirthYearView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectBirthYearView.swift new file mode 100644 index 0000000..86f46ab --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectBirthYearView.swift @@ -0,0 +1,18 @@ +// +// SelectBirthYearView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct SelectBirthYearView: View { + var body: some View { + Text("태어난 년도 선택") + } +} + +#Preview { + SelectBirthYearView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectGenderView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectGenderView.swift new file mode 100644 index 0000000..86c688d --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/SelectGenderView.swift @@ -0,0 +1,19 @@ +// +// SelectGenderView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct SelectGenderView: View { + var body: some View { + Text("성별 선택 뷰") + .navigationBarBackButtonHidden(true) + } +} + +#Preview { + SelectGenderView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift new file mode 100644 index 0000000..950ee2d --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift @@ -0,0 +1,18 @@ +// +// TermsOfServiceView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct TermsOfServiceView: View { + var body: some View { + Text("이용약관") + } +} + +#Preview { + TermsOfServiceView() +} diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/WriteNickNameView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/WriteNickNameView.swift new file mode 100644 index 0000000..23100e6 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/WriteNickNameView.swift @@ -0,0 +1,18 @@ +// +// WriteNickNameView.swift +// WithSuhyeon-iOS +// +// Created by 김예지 on 1/15/25. +// + +import SwiftUI + +struct WriteNickNameView: View { + var body: some View { + Text("닉네임 입력") + } +} + +#Preview { + WriteNickNameView() +} From e30a830b2b757cf4a2cbbb8585bf4ab14ea161f7 Mon Sep 17 00:00:00 2001 From: Yeji Date: Thu, 16 Jan 2025 01:46:22 +0900 Subject: [PATCH 2/6] feat: #26 feat TermsOfService view --- .../Component/WithSuhyeonSmallChip.swift | 4 +- .../Presentation/SignUp/SignUpContent.swift | 40 ++++++------ .../Presentation/SignUp/SignUpFeature.swift | 11 ++-- .../Presentation/SignUp/SignUpView.swift | 22 +++++-- .../SignUp/View/TermsOfServiceView.swift | 61 ++++++++++++++++++- 5 files changed, 104 insertions(+), 34 deletions(-) diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonSmallChip.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonSmallChip.swift index 6a8063d..8fcb426 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonSmallChip.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonSmallChip.swift @@ -47,7 +47,7 @@ struct WithSuhyeonSmallChip: View { } } -struct ChipTestView: View { +struct ChipTestView2: View { @State private var firstChipState: WithSuhyeonChipState = .unselected @State private var secondChipState: WithSuhyeonChipState = .selected @@ -78,5 +78,5 @@ struct ChipTestView: View { } #Preview { - ChipTestView() + ChipTestView2() } diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift index a48f763..dc484ab 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpContent.swift @@ -15,27 +15,25 @@ struct SignUpContent: View { } var body: some View { - HStack(spacing: 0) { - TabView(selection: $selectedTab) { - TermsOfServiceView() - .tag(SignUpContentCase.termsOfServiceView) - PhoneAuthenticationView() - .tag(SignUpContentCase.authenticationView) - WriteNickNameView() - .tag(SignUpContentCase.nickNameView) - SelectBirthYearView() - .tag(SignUpContentCase.birthYearView) - SelectGenderView() - .tag(SignUpContentCase.genderView) - ProfileImageView() - .tag(SignUpContentCase.profileImageView) - ActiveAreaView() - .tag(SignUpContentCase.activeAreaView) - } - .tabViewStyle(PageTabViewStyle()) - .onAppear { - UIScrollView.appearance().isScrollEnabled = false - } + TabView(selection: $selectedTab) { + TermsOfServiceView() + .tag(SignUpContentCase.termsOfServiceView) + PhoneAuthenticationView() + .tag(SignUpContentCase.authenticationView) + WriteNickNameView() + .tag(SignUpContentCase.nickNameView) + SelectBirthYearView() + .tag(SignUpContentCase.birthYearView) + SelectGenderView() + .tag(SignUpContentCase.genderView) + ProfileImageView() + .tag(SignUpContentCase.profileImageView) + ActiveAreaView() + .tag(SignUpContentCase.activeAreaView) + } + .tabViewStyle(PageTabViewStyle()) + .onAppear { + UIScrollView.appearance().isScrollEnabled = false } } } diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift index 5bba732..7a7797a 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift @@ -11,8 +11,7 @@ import Combine class SignUpFeature: Feature { struct State { var progress: Double = 0.0 - var isAgree: Bool = false // 약관 동의 - + var isAgree: Bool = false } enum Intent { @@ -57,7 +56,6 @@ class SignUpFeature: Feature { private func moveToNextStep() { if let currentIndex = SignUpContentCase.allCases.firstIndex(of: currentContent), currentIndex < SignUpContentCase.allCases.count - 1 { - print(currentIndex, "-" , currentContent) currentContent = SignUpContentCase.allCases[currentIndex + 1] updateProgress() } @@ -77,10 +75,11 @@ class SignUpFeature: Feature { } } - func changeSelectedContent(signUpContentCase: SignUpContentCase) { currentContent = signUpContentCase } + + func updateIsAgree(_ newValue: Bool) { + state.isAgree = newValue + } } - - diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift index 032bdc1..d949cbd 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift @@ -12,7 +12,7 @@ struct SignUpView: View { @StateObject private var signUpFeature = SignUpFeature() var body: some View { - VStack(alignment: .leading) { + VStack(alignment: .leading, spacing: 0) { WithSuhyeonTopNavigationBar(title: "", onTapLeft: {}) WithSuhyeonProgressBar(progress: signUpFeature.state.progress) @@ -24,10 +24,24 @@ struct SignUpView: View { SignUpContent(selectedTab: $signUpFeature.currentContent) - WithSuhyeonButton(title: "버튼", buttonState: .disabled, onTapButton: { - signUpFeature.send(.nextStep) - }) + WithSuhyeonButton( + title: "다음", + buttonState: isNextStepEnabled() ? .enabled : .disabled, + clickable: isNextStepEnabled(), + onTapButton: { + signUpFeature.send(.nextStep) + } + ) .padding(.horizontal, 16) + }.environmentObject(signUpFeature) + } + + private func isNextStepEnabled() -> Bool { + switch signUpFeature.currentContent { + case .termsOfServiceView: + return signUpFeature.state.isAgree + default: + return true } } } diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift index 950ee2d..630a299 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/View/TermsOfServiceView.swift @@ -8,8 +8,67 @@ import SwiftUI struct TermsOfServiceView: View { + @EnvironmentObject var signUpFeature: SignUpFeature + @State private var agreeStatus: [Bool] = [false, false, false] + var body: some View { - Text("이용약관") + VStack(alignment: .leading) { + WithSuhyeonCheckbox( + state: agreeStatus.allSatisfy { $0 } ? .checked : .unchecked, + placeholder: "모두 동의", + hasBackground: true + ) { + toggleAllChecks() + } + .padding(.bottom, 16) + + VStack(alignment: .leading, spacing: 16) { + ForEach(agreeStatus.indices, id: \.self) { index in + HStack(alignment: .top, spacing: 12) { + WithSuhyeonCheckbox( + state: agreeStatus[index] ? .checked : .unchecked, + placeholder: getPlaceholder(for: index), + hasBackground: false + ) { + toggleAgreeCheck(at: index) + } + WithSuhyeonUnderlineButton(title: "보기") {} + } + } + } + .padding(.horizontal, 20) + .padding(.vertical, 24) + .frame(maxWidth: .infinity, alignment: .topLeading) + .cornerRadius(20) + .overlay( + RoundedRectangle(cornerRadius: 20) + .stroke(Color.gray200, lineWidth: 1) + ) + Spacer() + } + .padding(.horizontal, 16) + .onChange(of: agreeStatus) { newStatus in + let allSelected = newStatus.allSatisfy { $0 } + signUpFeature.updateIsAgree(allSelected) + } + } + + private func toggleAllChecks() { + let newState = !agreeStatus.allSatisfy { $0 } + agreeStatus = Array(repeating: newState, count: agreeStatus.count) + } + + private func toggleAgreeCheck(at index: Int) { + agreeStatus[index].toggle() + } + + private func getPlaceholder(for index: Int) -> String { + switch index { + case 0: return "[필수] 만 18세 이상" + case 1: return "[필수] 이용약관 동의" + case 2: return "[필수] 개인정보 처리방침 동의" + default: return "" + } } } From a331be6d709c068e09ea4fc093ffb9fa5cc7225c Mon Sep 17 00:00:00 2001 From: Yeji Date: Thu, 16 Jan 2025 11:52:43 +0900 Subject: [PATCH 3/6] [chore] #26 Move button state logic into SignUpFeature --- .../Presentation/SignUp/SignUpFeature.swift | 41 +++++++++++++++++-- .../Presentation/SignUp/SignUpView.swift | 15 ++----- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift index 7a7797a..e83a418 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpFeature.swift @@ -12,11 +12,12 @@ class SignUpFeature: Feature { struct State { var progress: Double = 0.0 var isAgree: Bool = false + var buttonState: WithSuhyeonButtonState = .disabled } enum Intent { - case nextStep - case previousStep + case tapButton + case tapBackButton } enum SideEffect { @@ -32,6 +33,7 @@ class SignUpFeature: Feature { init() { bindIntents() updateProgress() + receiveState() } private func bindIntents() { @@ -40,15 +42,29 @@ class SignUpFeature: Feature { }.store(in: &cancellables) } + private func receiveState() { + $state.sink { [weak self] _ in + DispatchQueue.main.async { + self?.updateButtonState() + } + }.store(in: &cancellables) + + $currentContent.sink { [weak self] _ in + DispatchQueue.main.async { + self?.updateButtonState() + } + }.store(in: &cancellables) + } + func send(_ intent: Intent) { intentSubject.send(intent) } func handleIntent(_ intent: Intent) { switch intent { - case .nextStep: + case .tapButton: moveToNextStep() - case .previousStep: + case .tapBackButton: moveToPreviousStep() } } @@ -75,6 +91,23 @@ class SignUpFeature: Feature { } } + private func updateButtonState() { + let newButtonState: WithSuhyeonButtonState + + switch currentContent { + case .termsOfServiceView: + newButtonState = state.isAgree ? .enabled : .disabled + default: + newButtonState = .enabled + } + + guard state.buttonState != newButtonState else { + return + } + + state.buttonState = newButtonState + } + func changeSelectedContent(signUpContentCase: SignUpContentCase) { currentContent = signUpContentCase } diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift index d949cbd..c82ed9c 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift @@ -26,24 +26,15 @@ struct SignUpView: View { WithSuhyeonButton( title: "다음", - buttonState: isNextStepEnabled() ? .enabled : .disabled, - clickable: isNextStepEnabled(), + buttonState: signUpFeature.state.buttonState, + clickable: signUpFeature.state.buttonState == .enabled, onTapButton: { - signUpFeature.send(.nextStep) + signUpFeature.send(.tapButton) } ) .padding(.horizontal, 16) }.environmentObject(signUpFeature) } - - private func isNextStepEnabled() -> Bool { - switch signUpFeature.currentContent { - case .termsOfServiceView: - return signUpFeature.state.isAgree - default: - return true - } - } } #Preview { From bf6fece0f2bf63f48375cc58e64a6dea50460a3d Mon Sep 17 00:00:00 2001 From: Yeji Date: Thu, 16 Jan 2025 11:57:55 +0900 Subject: [PATCH 4/6] [feat] #26 Add back button intent --- .../WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift index c82ed9c..387fae5 100644 --- a/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Presentation/SignUp/SignUpView.swift @@ -13,7 +13,13 @@ struct SignUpView: View { var body: some View { VStack(alignment: .leading, spacing: 0) { - WithSuhyeonTopNavigationBar(title: "", onTapLeft: {}) + WithSuhyeonTopNavigationBar( + title: "", + onTapLeft: { + signUpFeature.send( + .tapBackButton + ) + }) WithSuhyeonProgressBar(progress: signUpFeature.state.progress) From 23c64e92528c517e983d4255b874d370d447fced Mon Sep 17 00:00:00 2001 From: sangwook Date: Wed, 15 Jan 2025 18:20:55 +0900 Subject: [PATCH 5/6] [feat] #28 with suhyeon location struct --- .../Foundation/WithSuhyeonLocation.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Foundation/WithSuhyeonLocation.swift diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Foundation/WithSuhyeonLocation.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Foundation/WithSuhyeonLocation.swift new file mode 100644 index 0000000..4966094 --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Foundation/WithSuhyeonLocation.swift @@ -0,0 +1,18 @@ +// +// WithSuhyeonLocation.swift +// WithSuhyeon-iOS +// +// Created by 우상욱 on 1/15/25. +// + +import Foundation + +struct WithSuhyeonLocation: Identifiable { + let id: UUID = UUID() + let location: String + let subLocation: [String] +} + +extension WithSuhyeonLocation { + static let location = [WithSuhyeonLocation(location: "서울", subLocation: ["서울1","서울2","서울3","서울4","서울5","서울6","서울7","서울8","서울9","서울10","서울11"]),WithSuhyeonLocation(location: "부산", subLocation: ["부산1","부산2","부산3","부산4","부산5","부산6","부산7","부산8","부산9","부산10","부산11",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),WithSuhyeonLocation(location: "서울", subLocation: ["서울","서울","서울","서울","서울","서울","서울","서울","서울","서울","서울",]),] +} From 2e1cb6667fa3d811138b432c4daf5bbc72d185f8 Mon Sep 17 00:00:00 2001 From: sangwook Date: Wed, 15 Jan 2025 18:21:35 +0900 Subject: [PATCH 6/6] [feat] #28 with suhyeon location select component --- .../Component/WithSuhyeonLocationSelect.swift | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonLocationSelect.swift diff --git a/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonLocationSelect.swift b/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonLocationSelect.swift new file mode 100644 index 0000000..e032f0e --- /dev/null +++ b/WithSuhyeon-iOS/WithSuhyeon-iOS/Core/DesignSystem/Component/WithSuhyeonLocationSelect.swift @@ -0,0 +1,94 @@ +// +// WithSuhyeonLocationSelect.swift +// WithSuhyeon-iOS +// +// Created by 우상욱 on 1/15/25. +// + +import SwiftUI + +struct WithSuhyeonLocationSelect: View { + let withSuhyeonLocation: [WithSuhyeonLocation] + let selectedMainLocationIndex: Int + let selectedSubLocationIndex: Int + let onTabSelected: (Int, Int) -> Void + + var body: some View { + HStack(spacing: 0) { + ScrollView { + VStack(spacing: 0) { + ForEach(withSuhyeonLocation.indices) { index in + ZStack { + Text(withSuhyeonLocation[index].location) + .font(.body02SB) + .foregroundColor(selectedMainLocationIndex == index ? Color.white : Color.black) + .padding(.vertical, 9) + .padding(.leading, 12) + .padding(.trailing, 42) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(selectedMainLocationIndex == index ? Color.primary500 : Color.clear) + ) + } + .frame(height: 50) + .contentShape(Rectangle()) + .onTapGesture { + if(selectedMainLocationIndex != index) { + onTabSelected(index, 0) + } + } + } + }.padding(.leading, 16) + .padding(.trailing, 8) + } + Divider() + .foregroundColor(.gray100) + ScrollView { + VStack(alignment: .leading, spacing: 0) { + ForEach(withSuhyeonLocation[selectedMainLocationIndex].subLocation.indices) { index in + ZStack(alignment: .leading) { + HStack { + Text(withSuhyeonLocation[selectedMainLocationIndex].subLocation[index]) + .font(.body03SB) + .foregroundColor(.gray500) + Spacer() + } + .padding(.leading, 12) + .padding(.vertical, 10) + .background( + RoundedRectangle(cornerRadius: 12) + .fill(selectedSubLocationIndex == index ? Color.primary50 : Color.clear) + ) + .frame(width: .infinity) + .contentShape(Rectangle()) + .onTapGesture { + onTabSelected(selectedMainLocationIndex, index) + } + } + .frame(height: 50) + .padding(.leading, 8) + .padding(.trailing, 16) + } + } + } + }.frame(height: .infinity) + } +} + +struct locationPreview: View { + @State var selectedMainLocationIndex: Int = 0 + @State var selectedSubLocationIndex: Int = 0 + + var body: some View { + VStack { + WithSuhyeonLocationSelect(withSuhyeonLocation: WithSuhyeonLocation.location, selectedMainLocationIndex: selectedMainLocationIndex, selectedSubLocationIndex: selectedSubLocationIndex, onTabSelected: { num1, num2 in + selectedMainLocationIndex = num1 + selectedSubLocationIndex = num2 + }) + }.frame(height: 400) + } +} + +#Preview { + locationPreview() +}