From 2fba81d941147eed4b62cfe5f080259ae0cf72fd Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 19 Jun 2024 10:01:19 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8[Feat]=20Main=20Login=20View?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Login/Main/LoginButton.swift | 79 +++++++++++++++++++ .../Presentation/Login/Main/LoginView.swift | 60 ++++++++++++++ .../Presentation/Login/Term/TermView.swift | 19 +++++ .../Assets.xcassets/Color/Login/Contents.json | 6 ++ .../Color/Login/Kakao.colorset/Contents.json | 20 +++++ .../Login/ic_apple.imageset/Contents.json | 12 +++ .../Login/ic_apple.imageset/ic_apple.svg | 9 +++ .../Login/ic_kakao.imageset/Contents.json | 12 +++ .../Login/ic_kakao.imageset/ic_kakao.svg | 9 +++ .../Assets/Common/Common+Assests.swift | 3 + .../DesignSystem/Sources/Color/Color.swift | 2 + 11 files changed, 231 insertions(+) create mode 100644 Projects/App/Sources/Presentation/Login/Main/LoginButton.swift create mode 100644 Projects/App/Sources/Presentation/Login/Main/LoginView.swift create mode 100644 Projects/App/Sources/Presentation/Login/Term/TermView.swift create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Kakao.colorset/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/ic_apple.svg create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/ic_kakao.svg diff --git a/Projects/App/Sources/Presentation/Login/Main/LoginButton.swift b/Projects/App/Sources/Presentation/Login/Main/LoginButton.swift new file mode 100644 index 0000000..30e17ae --- /dev/null +++ b/Projects/App/Sources/Presentation/Login/Main/LoginButton.swift @@ -0,0 +1,79 @@ +// +// LoginButton.swift +// App +// +// Created by 박서연 on 2024/06/18. +// Copyright © 2024 iOS. All rights reserved. +// + +import SwiftUI +import DesignSystem + +enum Login: String, CaseIterable { + case kakao + case apple + + var image: Image { + switch self { + case .kakao: + return ZerosomeAsset.ic_kakako + case .apple: + return ZerosomeAsset.ic_apple + } + } + + var backgroundColor: Color { + switch self { + case .kakao: + return Color.kakao + case .apple: + return Color.black + } + } + + var titleColor: Color { + switch self { + case .kakao: + return Color.neutral800 + case .apple: + return Color.white + } + } + + var title: String { + switch self { + case .kakao: + "카카오 ID로 로그인" + case .apple: + "Apple ID로 로그인" + } + } +} + +struct LoginButton: View { + let type: Login + + init(type: Login) { + self.type = type + } + + var body: some View { + HStack(spacing: 0) { + type.image + .frame(width: 24, height: 24) + + Text(type.title) + .frame(maxWidth: .infinity, alignment: .center) + } + .padding(.init(top: 15, leading: 15, bottom: 15, trailing: 0)) + .applyFont(font: .subtitle1) + .foregroundStyle(type.titleColor) + .background(type.backgroundColor) + .clipShape(RoundedRectangle(cornerRadius: 10)) + } +} + + +#Preview { + LoginButton(type: .apple) +} diff --git a/Projects/App/Sources/Presentation/Login/Main/LoginView.swift b/Projects/App/Sources/Presentation/Login/Main/LoginView.swift new file mode 100644 index 0000000..7a3ec92 --- /dev/null +++ b/Projects/App/Sources/Presentation/Login/Main/LoginView.swift @@ -0,0 +1,60 @@ +// +// LoginView.swift +// App +// +// Created by 박서연 on 2024/06/18. +// Copyright © 2024 iOS. All rights reserved. +// + +import SwiftUI +import Kingfisher +import DesignSystem + + + +struct LoginView: View { + var body: some View { + ZStack { + Color.primaryFF6972 + .ignoresSafeArea() + + VStack { + ZerosomeAsset.zero_progress + .frame(width: 240, height: 240) + .clipShape(RoundedRectangle(cornerRadius: 40)) + + Spacer().frame(height: 123) + + VStack(spacing: 12) { + ForEach(Login.allCases, id:\.self) { type in + LoginButton(type: type) + .onTapGesture { + switch type { + case .apple: + print("apple Login") + case .kakao: + print("kakao Login") + } + } + } + } + .padding(.horizontal, 24) + .padding(.bottom, 20) + + VStack(spacing: 2) { + Text("일단 둘러볼게요") + .applyFont(font: .body2) + .foregroundStyle(Color.white) + .frame(maxWidth: .infinity, alignment: .center) + + DivideRectangle(height: 1, color: .white) + .frame(width: 88) + } + } + } + } +} + +#Preview { + LoginView() +} diff --git a/Projects/App/Sources/Presentation/Login/Term/TermView.swift b/Projects/App/Sources/Presentation/Login/Term/TermView.swift new file mode 100644 index 0000000..e4650ec --- /dev/null +++ b/Projects/App/Sources/Presentation/Login/Term/TermView.swift @@ -0,0 +1,19 @@ +// +// TermView.swift +// App +// +// Created by 박서연 on 2024/06/19. +// Copyright © 2024 iOS. All rights reserved. +// + +import SwiftUI + +struct TermView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + TermView() +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Kakao.colorset/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Kakao.colorset/Contents.json new file mode 100644 index 0000000..126cc45 --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/Kakao.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.898", + "red" : "0.996" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/Contents.json new file mode 100644 index 0000000..f91b0af --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_apple.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/ic_apple.svg b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/ic_apple.svg new file mode 100644 index 0000000..4e9dac5 --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_apple.imageset/ic_apple.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/Contents.json new file mode 100644 index 0000000..58dfe96 --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_kakao.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/ic_kakao.svg b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/ic_kakao.svg new file mode 100644 index 0000000..af9bffa --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Color/Login/ic_kakao.imageset/ic_kakao.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift b/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift index 0e595fe..953d33d 100644 --- a/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift +++ b/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift @@ -10,4 +10,7 @@ import SwiftUI public struct ZerosomeAsset { public static let zero_progress = DesignSystemAsset.Assets.progressGray.swiftUIImage + + public static let ic_kakako = DesignSystemAsset.Assets.icKakao.swiftUIImage + public static let ic_apple = DesignSystemAsset.Assets.icApple.swiftUIImage } diff --git a/Projects/DesignSystem/Sources/Color/Color.swift b/Projects/DesignSystem/Sources/Color/Color.swift index f13e789..0046a7c 100644 --- a/Projects/DesignSystem/Sources/Color/Color.swift +++ b/Projects/DesignSystem/Sources/Color/Color.swift @@ -27,4 +27,6 @@ extension Color { public static let caution = Color(asset: DesignSystemAsset.Assets.caution) public static let negative = Color(asset: DesignSystemAsset.Assets.negative) public static let information = Color(asset: DesignSystemAsset.Assets.information) + + public static let kakao = Color(asset: DesignSystemAsset.Assets.kakao) } From aaf83e26770a87f7ce55ea0575ce00035eec02f6 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Wed, 19 Jun 2024 10:50:50 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20[Feat]=20Generate=20Login=20&?= =?UTF-8?q?=20Term=20View?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인 및 약관 화면 구현 - 백버튼 미적용 - 모두 동의 액션에 대한 의논 필요 --- .../Login/Term/SingleTermView.swift | 48 +++++++ .../Presentation/Login/Term/TermView.swift | 120 +++++++++++++++++- .../Router/Ex+View+Navigation.swift | 3 +- .../ic_arrow_back_gray.imageset/Contents.json | 21 +++ .../ic_arrow_back_gray.svg | 3 + .../Contents.json | 21 +++ .../ic_circle_check_gray.svg | 4 + .../Contents.json | 21 +++ .../ic_circle_check_primary.svg | 4 + .../Assets/Common/Common+Assests.swift | 4 + 10 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 Projects/App/Sources/Presentation/Login/Term/SingleTermView.swift create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/ic_arrow_back_gray.svg create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/ic_circle_check_gray.svg create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/Contents.json create mode 100644 Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/ic_circle_check_primary.svg diff --git a/Projects/App/Sources/Presentation/Login/Term/SingleTermView.swift b/Projects/App/Sources/Presentation/Login/Term/SingleTermView.swift new file mode 100644 index 0000000..ff9dcfe --- /dev/null +++ b/Projects/App/Sources/Presentation/Login/Term/SingleTermView.swift @@ -0,0 +1,48 @@ +// +// SingleTermView.swift +// App +// +// Created by 박서연 on 2024/06/19. +// Copyright © 2024 iOS. All rights reserved. +// + +import SwiftUI +import DesignSystem + +struct SingleTermView: View { + @Binding var isChecked: Bool + var term: Term + var tapTitle: (Term) -> Void + + var body: some View { + HStack(spacing: 12) { + ( + isChecked + ? ZerosomeAsset.ic_check_circle_primary + : ZerosomeAsset.ic_check_circle_gray + ) + .resizable() + .frame(width: 24, height: 24) + .onTapGesture { + isChecked.toggle() + } + + Text(term.title) + .applyFont(font: .body1) + .foregroundStyle(Color.neutral800) + .frame(maxWidth: .infinity, alignment: .leading) + + Spacer() + Text("보기") + .applyFont(font: .body2) + .foregroundStyle(Color.neutral400) + .onTapGesture { + tapTitle(term) + } + } + } +} + +#Preview { + SingleTermView(isChecked: .constant(true), term: .term, tapTitle: { _ in print("tapped")}) +} diff --git a/Projects/App/Sources/Presentation/Login/Term/TermView.swift b/Projects/App/Sources/Presentation/Login/Term/TermView.swift index e4650ec..396d0da 100644 --- a/Projects/App/Sources/Presentation/Login/Term/TermView.swift +++ b/Projects/App/Sources/Presentation/Login/Term/TermView.swift @@ -6,14 +6,130 @@ // Copyright © 2024 iOS. All rights reserved. // +import Combine +import DesignSystem import SwiftUI +enum Term: CaseIterable{ + case term + case personalInfo + case marketing + + var title: String { + switch self { + case .term: + return "(필수) 서비스 이용약관 동의" + case .personalInfo: + return "(필수) 개인정보 처리방침 동의" + case .marketing: + return "(선택) 마케팅 수신 동의" + } + } +} + +final class TermViewModel: ObservableObject { + @Published var isAllChecked: Bool = false + @Published var isTermChecked: Bool = false + @Published var isPersonalChecked: Bool = false + @Published var isMarketingChecked: Bool = false + + private var cancellables = [AnyCancellable]() + + init() { + Publishers.CombineLatest3($isTermChecked, $isPersonalChecked, $isMarketingChecked) + .sink { [weak self] term, personalInfo, marketing in + self?.isAllChecked = term && personalInfo || marketing + } + .store(in: &cancellables) + } + + func toggleAll() { + if isTermChecked && isPersonalChecked && isMarketingChecked { + isTermChecked = false + isPersonalChecked = false + isMarketingChecked = false + } else { + isTermChecked = true + isPersonalChecked = true + isMarketingChecked = true + } + } +} + struct TermView: View { + @StateObject var viewModel = TermViewModel() + @State private var isTermChecked: Bool = false + var body: some View { - Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + VStack(alignment:.leading, spacing: 30) { + + VStack(alignment:.leading, spacing: 6) { + Text("약관에 동의해 주세요") + .applyFont(font: .heading1) + Text("여러분의 개인정보와 서비스 이용 권리\n잘 지켜드릴게요") + .applyFont(font: .body2) + .foregroundStyle(Color.neutral500) + } + + VStack(alignment: .leading, spacing: 18) { + HStack(spacing: 12) { + ( + viewModel.isAllChecked + ? ZerosomeAsset.ic_check_circle_primary + : ZerosomeAsset.ic_check_circle_gray + ) + .onTapGesture { + viewModel.toggleAll() + } + + VStack(alignment:.leading, spacing: 2) { + Text("모두 동의") + .applyFont(font: .heading1) + Text("서비스 이용을 위해 아래 약관에 모두 동의합니다.") + .applyFont(font: .body2) + .foregroundStyle(Color.neutral500) + } + } + + DivideRectangle(height: 1, color: Color.neutral100) + .padding(.bottom, 3) + + SingleTermView( + isChecked: $viewModel.isTermChecked, + term: .term) { term in + print("이용약관 check") + } + + SingleTermView( + isChecked: $viewModel.isPersonalChecked, + term: .personalInfo) { term in + print("개인정보 check") + } + + SingleTermView( + isChecked: $viewModel.isMarketingChecked, + term: .marketing) { term in + print("마케팅 check") + } + } + + Spacer() + + CommonButton(title: "다음", font: .subtitle1) + .enable(viewModel.isAllChecked) + .tap { + print("button tapped..") + } + } + .padding(.horizontal, 22) + .navigationBackButton { + + } } } #Preview { - TermView() + NavigationStack { + TermView() + } } diff --git a/Projects/App/Sources/Presentation/Tabbar+Navigation/Router/Ex+View+Navigation.swift b/Projects/App/Sources/Presentation/Tabbar+Navigation/Router/Ex+View+Navigation.swift index 844f441..f9dfca2 100644 --- a/Projects/App/Sources/Presentation/Tabbar+Navigation/Router/Ex+View+Navigation.swift +++ b/Projects/App/Sources/Presentation/Tabbar+Navigation/Router/Ex+View+Navigation.swift @@ -7,6 +7,7 @@ // import SwiftUI +import DesignSystem public extension View { func navigationBackButton(_ action: @escaping () -> Void) -> some View { @@ -16,7 +17,7 @@ public extension View { Button(action: action, label: { // TODO: - 백버튼 이미지 수정 - Image("arrowBack") + ZerosomeAsset.ic_back_button .resizable() .frame(width: 24, height: 24) }) diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/Contents.json new file mode 100644 index 0000000..a89c74b --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_arrow_back_gray.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/ic_arrow_back_gray.svg b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/ic_arrow_back_gray.svg new file mode 100644 index 0000000..5e6a6dc --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_arrow_back_gray.imageset/ic_arrow_back_gray.svg @@ -0,0 +1,3 @@ + + + diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/Contents.json new file mode 100644 index 0000000..2a46ae1 --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_circle_check_gray.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/ic_circle_check_gray.svg b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/ic_circle_check_gray.svg new file mode 100644 index 0000000..6ccc1ae --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_gray.imageset/ic_circle_check_gray.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/Contents.json b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/Contents.json new file mode 100644 index 0000000..19d574b --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ic_circle_check_primary.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/ic_circle_check_primary.svg b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/ic_circle_check_primary.svg new file mode 100644 index 0000000..b39ba7e --- /dev/null +++ b/Projects/DesignSystem/Resources/Assets.xcassets/Common/ic_circle_check_primary.imageset/ic_circle_check_primary.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift b/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift index 953d33d..0f7d1df 100644 --- a/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift +++ b/Projects/DesignSystem/Sources/Assets/Common/Common+Assests.swift @@ -13,4 +13,8 @@ public struct ZerosomeAsset { public static let ic_kakako = DesignSystemAsset.Assets.icKakao.swiftUIImage public static let ic_apple = DesignSystemAsset.Assets.icApple.swiftUIImage + public static let ic_check_circle_gray = DesignSystemAsset.Assets.icCircleCheckGray.swiftUIImage + public static let ic_check_circle_primary = DesignSystemAsset.Assets.icCircleCheckPrimary.swiftUIImage + + public static let ic_back_button = DesignSystemAsset.Assets.icArrowBackGray.swiftUIImage } From e6d2461d8995ee0aa7224ed4b420d3d6a9744810 Mon Sep 17 00:00:00 2001 From: Park Seo Yeon Date: Thu, 20 Jun 2024 12:38:40 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20[Feat]=20Generate=20Nickname=20?= =?UTF-8?q?View?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Login/Nickname/NicknameView.swift | 71 ++++++++++++ .../Sources/Component/Common/TextInput.swift | 106 ++++++++++++++++++ Projects/DesignSystem/Sources/Font/Font.swift | 4 + 3 files changed, 181 insertions(+) create mode 100644 Projects/App/Sources/Presentation/Login/Nickname/NicknameView.swift create mode 100644 Projects/DesignSystem/Sources/Component/Common/TextInput.swift diff --git a/Projects/App/Sources/Presentation/Login/Nickname/NicknameView.swift b/Projects/App/Sources/Presentation/Login/Nickname/NicknameView.swift new file mode 100644 index 0000000..f170657 --- /dev/null +++ b/Projects/App/Sources/Presentation/Login/Nickname/NicknameView.swift @@ -0,0 +1,71 @@ +// +// NicknameView.swift +// App +// +// Created by 박서연 on 2024/06/20. +// Copyright © 2024 iOS. All rights reserved. +// +import DesignSystem +import SwiftUI + +class NicknameViewModel: ObservableObject { + @Published var isValid: Bool = false + + func checkNickName(completion: @escaping (Bool) -> Void) { + // 닉네임 중복체크 + } +} + +struct NicknameView: View { + @StateObject var viewModel = NicknameViewModel() + @State private var text: String = "" + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text("닉네임을 설정해 주세요") + .applyFont(font: .heading1) + Text("최소 2자 ~ 12자 이내의 닉네임을 입력해 주세요\n한글/영문/숫자/특수문자 모두 가능해요") + .applyFont(font: .body2) + .foregroundStyle(Color.neutral500) + + Spacer().frame(height: 24) + + TextInput(text: $text) + .placeholder("닉네임을 입력해 주세요") + .maxCount(12) + .setError( + // TODO: - 서버 통신 및 에러 문구 타이밍 의논 + viewModel.isValid + ) + .padding(.bottom, 4) + + if viewModel.isValid { + Text("사용 가능한 닉네임입니다.") + .applyFont(font: .body4) + .foregroundStyle(Color.positive) + .padding(.leading, 12) + } else { + Text("이미 사용중인 닉네임입니다.") + .applyFont(font: .body4) + .foregroundStyle(Color.negative) + .padding(.leading, 12) + } + + Spacer() + + CommonButton(title: "회원가입 완료", font: .subtitle1) + .enable(viewModel.isValid) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 22) + .navigationBackButton { + // TODO: - router 추가 + } + } +} + +#Preview { + NavigationStack { + NicknameView() + } +} diff --git a/Projects/DesignSystem/Sources/Component/Common/TextInput.swift b/Projects/DesignSystem/Sources/Component/Common/TextInput.swift new file mode 100644 index 0000000..e72e2d8 --- /dev/null +++ b/Projects/DesignSystem/Sources/Component/Common/TextInput.swift @@ -0,0 +1,106 @@ +// +// TextInput.swift +// DesignSystem +// +// Created by 박서연 on 2024/06/20. +// Copyright © 2024 iOS. All rights reserved. +// + +import SwiftUI + +public struct TextInput: View { + public var text: Binding + public var placeholder: String = "" + public var trailingImage: Image? + public var tapTrailingImage: (() -> Void)? + public var maxCount: Int? + public var minCount: Int? + public var isError: Bool = false + public var font: ZSFont = .body2 + public var placeholderFont: ZSFont = .body2 + + public init(text: Binding) { + self.text = text + } + + public var body: some View { + HStack(spacing: 0) { + TextField( + "", + text: text, + prompt: Text(placeholder) + .font(placeholderFont.toFont) + .foregroundColor(Color.gray), + + axis: .horizontal + ) + .font(font.toFont) + .foregroundStyle(getTextColor()) + .padding(.init(top: 13, leading: 12, bottom: 13, trailing: 12)) + .disableAutocorrection(true) + .onReceive(text.wrappedValue.publisher.collect()) { + if let maxCount { + let s = String($0.prefix(maxCount)) + if text.wrappedValue != s && (maxCount != 0) { + text.wrappedValue = s + } + } + } + + if text.wrappedValue.count > 0, tapTrailingImage != nil, trailingImage != nil { + trailingImage! + .resizable() + .frame(width: 20, height: 20) + .padding(14) + .onTapGesture { + tapTrailingImage?() + } + } + }.background( + RoundedRectangle(cornerRadius: 10) + .stroke(Color.neutral300, lineWidth: 1) + ) + } + + private func getTextColor() -> Color { + return Color.neutral700 + } +} + +public extension TextInput { + func maxCount(_ count: Int) -> Self { + var copy = self + copy.maxCount = count + return copy + } + + func setError(_ isError: Bool) -> Self { + var copy = self + copy.isError = isError + return copy + } + + func tapTrailingImage(action: @escaping (() -> Void)) -> Self { + var copy = self + copy.tapTrailingImage = action + return copy + } + + func font(_ font: ZSFont) -> Self { + var copy = self + copy.font = font + return copy + } + + func placeholder(_ placeholder: String) -> Self { + var copy = self + copy.placeholder = placeholder + return copy + } + + func placeholderFont(_ font: ZSFont) -> Self { + var copy = self + copy.placeholderFont = font + return copy + } +} diff --git a/Projects/DesignSystem/Sources/Font/Font.swift b/Projects/DesignSystem/Sources/Font/Font.swift index bcf460e..9788687 100644 --- a/Projects/DesignSystem/Sources/Font/Font.swift +++ b/Projects/DesignSystem/Sources/Font/Font.swift @@ -134,6 +134,10 @@ extension ZSFont { public var toUIFont: UIFont { return UIFont(name: self.name, size: self.size) ?? UIFont.systemFont(ofSize: self.size) } + + public var toFont: Font { + return Font.custom(self.name, size: self.size) + } } public struct FontModifier: ViewModifier {