From 711ffbcb83b6f42d3fda32820c3ffcedd4155959 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Mon, 31 Mar 2025 21:12:24 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=B0=BE=EA=B8=B0,=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20-=20TextField=20=ED=84=B0?= =?UTF-8?q?=EC=B9=98=EC=98=81=EC=97=AD=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../User/FindPassword/FindPasswordView.swift | 8 ++++ SodaLive/Sources/User/Login/LoginView.swift | 38 +++++++++++++-- SodaLive/Sources/User/SignUp/SignUpView.swift | 48 +++++++++++++++++-- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/SodaLive/Sources/User/FindPassword/FindPasswordView.swift b/SodaLive/Sources/User/FindPassword/FindPasswordView.swift index 9a3fdbb..0868348 100644 --- a/SodaLive/Sources/User/FindPassword/FindPasswordView.swift +++ b/SodaLive/Sources/User/FindPassword/FindPasswordView.swift @@ -11,6 +11,7 @@ struct FindPasswordView: View { @StateObject var viewModel = FindPasswordViewModel() @StateObject var keyboardHandler = KeyboardHandler() + @FocusState private var isFocused: Bool var body: some View { BaseView(isLoading: $viewModel.isLoading) { @@ -37,6 +38,7 @@ struct FindPasswordView: View { .padding(.horizontal, 26.7) TextField("이메일을 입력하세요", text: $viewModel.email) + .focused($isFocused) .autocapitalization(.none) .disableAutocorrection(true) .font(.custom(Font.medium.rawValue, size: 15)) @@ -48,6 +50,9 @@ struct FindPasswordView: View { .background(RoundedRectangle(cornerRadius: 6.7).fill(Color.gray33.opacity(0.7))) .padding(.top, 40) .padding(.horizontal, 26.7) + .onTapGesture { + isFocused = true + } Text("임시 비밀번호 받기") .font(.custom(Font.bold.rawValue, size: 18.3)) @@ -79,6 +84,9 @@ struct FindPasswordView: View { } } } + .onAppear { + isFocused = true + } } } .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) { diff --git a/SodaLive/Sources/User/Login/LoginView.swift b/SodaLive/Sources/User/Login/LoginView.swift index bde598f..f0c0979 100644 --- a/SodaLive/Sources/User/Login/LoginView.swift +++ b/SodaLive/Sources/User/Login/LoginView.swift @@ -10,9 +10,14 @@ import PopupView struct LoginView: View { + enum FocusField: Hashable { + case email, password + } + @ObservedObject var viewModel = LoginViewModel() @State private var isPasswordVisible: Bool = false + @FocusState private var focusedField: FocusField? var body: some View { BaseView(isLoading: $viewModel.isLoading) { @@ -22,6 +27,8 @@ struct LoginView: View { Spacer() TextField("이메일", text: $viewModel.email) + .submitLabel(.next) + .focused($focusedField, equals: .email) .autocapitalization(.none) .disableAutocorrection(true) .font(.custom(Font.medium.rawValue, size: 15)) @@ -32,6 +39,12 @@ struct LoginView: View { .frame(height: 56) .background(RoundedRectangle(cornerRadius: 6.7).fill(Color.gray33.opacity(0.7))) .padding(.horizontal, 13.3) + .onSubmit { + self.focusedField = .password + } + .onTapGesture { + self.focusedField = .email + } HStack { Group { @@ -41,6 +54,8 @@ struct LoginView: View { SecureField("비밀번호", text: $viewModel.password) } } + .submitLabel(.done) + .focused($focusedField, equals: .password) .autocapitalization(.none) .disableAutocorrection(true) .font(.custom(Font.medium.rawValue, size: 15)) @@ -60,8 +75,16 @@ struct LoginView: View { .background(RoundedRectangle(cornerRadius: 6.7).fill(Color.gray33.opacity(0.7))) .padding(.horizontal, 13.3) .padding(.top, 16) + .onTapGesture { + self.focusedField = .password + } - Button(action: { viewModel.login() }) { + Button( + action: { + hideKeyboard() + viewModel.login() + } + ) { Text("로그인") .font(.custom(Font.bold.rawValue, size: 15)) .frame(width: screenSize().width - 26.6, height: 46.7) @@ -75,18 +98,27 @@ struct LoginView: View { .font(.custom(Font.medium.rawValue, size: 13.3)) .foregroundColor(Color.graybb) .padding(.vertical, 10) - .onTapGesture { AppState.shared.setAppStep(step: .findPassword) } + .onTapGesture { + hideKeyboard() + AppState.shared.setAppStep(step: .findPassword) + } .padding(.top, 30) Text("보이스온 회원이 아닌가요? 지금 가입하세요.") .font(.custom(Font.medium.rawValue, size: 13.3)) .foregroundColor(Color.graybb) .padding(.vertical, 10) - .onTapGesture { AppState.shared.setAppStep(step: .signUp) } + .onTapGesture { + hideKeyboard() + AppState.shared.setAppStep(step: .signUp) + } .padding(.top, 20) Spacer() } + .onAppear { + self.focusedField = .email + } } .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in diff --git a/SodaLive/Sources/User/SignUp/SignUpView.swift b/SodaLive/Sources/User/SignUp/SignUpView.swift index 9487b80..347f03e 100644 --- a/SodaLive/Sources/User/SignUp/SignUpView.swift +++ b/SodaLive/Sources/User/SignUp/SignUpView.swift @@ -9,10 +9,16 @@ import SwiftUI struct SignUpView: View { + enum FocusField: Hashable { + case email, password + } + @ObservedObject var viewModel = SignUpViewModel() @StateObject var keyboardHandler = KeyboardHandler() @State private var isPasswordVisible: Bool = false + @FocusState private var focusedField: FocusField? + var body: some View { BaseView(isLoading: $viewModel.isLoading) { GeometryReader { proxy in @@ -22,6 +28,8 @@ struct SignUpView: View { ScrollView(.vertical, showsIndicators: false) { VStack(spacing: 0) { TextField("이메일", text: $viewModel.email) + .submitLabel(.next) + .focused($focusedField, equals: .email) .autocapitalization(.none) .disableAutocorrection(true) .font(.custom(Font.medium.rawValue, size: 15)) @@ -33,6 +41,12 @@ struct SignUpView: View { .background(RoundedRectangle(cornerRadius: 6.7).fill(Color.gray33.opacity(0.7))) .padding(.horizontal, 13.3) .padding(.top, 13.3) + .onSubmit { + self.focusedField = .password + } + .onTapGesture { + self.focusedField = .email + } HStack { Group { @@ -42,6 +56,8 @@ struct SignUpView: View { SecureField("비밀번호", text: $viewModel.password) } } + .submitLabel(.done) + .focused($focusedField, equals: .password) .autocapitalization(.none) .disableAutocorrection(true) .font(.custom(Font.medium.rawValue, size: 15)) @@ -52,6 +68,7 @@ struct SignUpView: View { Image(systemName: isPasswordVisible ? "eye.slash.fill" : "eye.fill") .foregroundColor(.grayee) .onTapGesture { + hideKeyboard() isPasswordVisible.toggle() } } @@ -61,6 +78,9 @@ struct SignUpView: View { .background(RoundedRectangle(cornerRadius: 6.7).fill(Color.gray33.opacity(0.7))) .padding(.horizontal, 13.3) .padding(.top, 16) + .onTapGesture { + self.focusedField = .password + } VStack(spacing: 5) { HStack(spacing: 10) { @@ -70,7 +90,10 @@ struct SignUpView: View { "btn_select_normal" ) .padding(10) - .onTapGesture { viewModel.isAgreeTerms.toggle() } + .onTapGesture { + hideKeyboard() + viewModel.isAgreeTerms.toggle() + } HStack(spacing: 5) { Text("이용약관") @@ -88,7 +111,10 @@ struct SignUpView: View { Spacer() } .contentShape(Rectangle()) - .onTapGesture { viewModel.isAgreeTerms.toggle() } + .onTapGesture { + hideKeyboard() + viewModel.isAgreeTerms.toggle() + } HStack(spacing: 10) { Image( @@ -97,7 +123,10 @@ struct SignUpView: View { "btn_select_normal" ) .padding(10) - .onTapGesture { viewModel.isAgreePrivacyPolicy.toggle() } + .onTapGesture { + hideKeyboard() + viewModel.isAgreePrivacyPolicy.toggle() + } HStack(spacing: 5) { Text("개인정보수집 및 이용동의") @@ -115,7 +144,10 @@ struct SignUpView: View { Spacer() } .contentShape(Rectangle()) - .onTapGesture { viewModel.isAgreePrivacyPolicy.toggle() } + .onTapGesture { + hideKeyboard() + viewModel.isAgreePrivacyPolicy.toggle() + } } .padding(.top, 20) .padding(.horizontal, 13.3) @@ -129,7 +161,10 @@ struct SignUpView: View { .cornerRadius(10) .padding(.horizontal, 13.3) .contentShape(Rectangle()) - .onTapGesture { viewModel.signUp() } + .onTapGesture { + hideKeyboard() + viewModel.signUp() + } .padding(.top, 26.7) Rectangle() @@ -145,6 +180,9 @@ struct SignUpView: View { } } .edgesIgnoringSafeArea(.bottom) + .onAppear { + focusedField = .email + } .onTapGesture { hideKeyboard() }