From 2419da8320b4fd0e3431c3f751b1c80561427a5a Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Thu, 9 Feb 2023 16:05:12 +0300 Subject: [PATCH 01/11] refactor code --- .../iosSample.xcodeproj/project.pbxproj | 6 +- .../iosSample/Input/VisualExtentions.swift | 82 +++++++++++-------- .../iosSample/Input/VisualFormatter.swift | 10 ++- .../State/UnsafeObservableState.swift | 8 +- .../iosSample/iosSample/View/FormView.swift | 31 +++---- .../View/SecureTextFieldWithControl.swift | 70 ---------------- .../iosSample/View/SubmitButtonView.swift | 14 +--- .../iosSample/View/TextFieldWithControl.swift | 77 ++++++++++------- .../iosSample/iosSample/View/ToggleView.swift | 9 +- sample/iosSample/iosSample/iosSampleApp.swift | 4 +- 10 files changed, 130 insertions(+), 181 deletions(-) delete mode 100644 sample/iosSample/iosSample/View/SecureTextFieldWithControl.swift diff --git a/sample/iosSample/iosSample.xcodeproj/project.pbxproj b/sample/iosSample/iosSample.xcodeproj/project.pbxproj index 06c8c03..94b6d8a 100644 --- a/sample/iosSample/iosSample.xcodeproj/project.pbxproj +++ b/sample/iosSample/iosSample.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ EC7C20172987F483001DCE8B /* UnsafeObservableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C20162987F483001DCE8B /* UnsafeObservableState.swift */; }; EC7C201B298A6A67001DCE8B /* VisualFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C201A298A6A66001DCE8B /* VisualFormatter.swift */; }; EC7C201E298A6AD1001DCE8B /* VisualExtentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C201D298A6AD1001DCE8B /* VisualExtentions.swift */; }; - EC7C2020298A6B76001DCE8B /* SecureTextFieldWithControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C201F298A6B76001DCE8B /* SecureTextFieldWithControl.swift */; }; EC7C2023298A710B001DCE8B /* ToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C2022298A710B001DCE8B /* ToggleView.swift */; }; EC7C2025298A7883001DCE8B /* SubmitButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C2024298A7883001DCE8B /* SubmitButtonView.swift */; }; ECB57B3B298CFC4E00AE547C /* FormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB57B3A298CFC4E00AE547C /* FormView.swift */; }; @@ -29,7 +28,6 @@ EC7C20162987F483001DCE8B /* UnsafeObservableState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsafeObservableState.swift; sourceTree = ""; }; EC7C201A298A6A66001DCE8B /* VisualFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualFormatter.swift; sourceTree = ""; }; EC7C201D298A6AD1001DCE8B /* VisualExtentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualExtentions.swift; sourceTree = ""; }; - EC7C201F298A6B76001DCE8B /* SecureTextFieldWithControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureTextFieldWithControl.swift; sourceTree = ""; }; EC7C2022298A710B001DCE8B /* ToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleView.swift; sourceTree = ""; }; EC7C2024298A7883001DCE8B /* SubmitButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitButtonView.swift; sourceTree = ""; }; ECB57B3A298CFC4E00AE547C /* FormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormView.swift; sourceTree = ""; }; @@ -103,7 +101,6 @@ EC7C2021298A70E7001DCE8B /* View */ = { isa = PBXGroup; children = ( - EC7C201F298A6B76001DCE8B /* SecureTextFieldWithControl.swift */, EC7C1FEB2987B251001DCE8B /* TextFieldWithControl.swift */, EC7C2022298A710B001DCE8B /* ToggleView.swift */, EC7C2024298A7883001DCE8B /* SubmitButtonView.swift */, @@ -208,7 +205,6 @@ EC7C1FEC2987B251001DCE8B /* TextFieldWithControl.swift in Sources */, EC7C2025298A7883001DCE8B /* SubmitButtonView.swift in Sources */, EC7C20172987F483001DCE8B /* UnsafeObservableState.swift in Sources */, - EC7C2020298A6B76001DCE8B /* SecureTextFieldWithControl.swift in Sources */, ECB57B3B298CFC4E00AE547C /* FormView.swift in Sources */, EC7C201E298A6AD1001DCE8B /* VisualExtentions.swift in Sources */, EC7C1FEA2987B251001DCE8B /* iosSampleApp.swift in Sources */, @@ -340,6 +336,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"iosSample/Preview Content\""; + DEVELOPMENT_TEAM = 4778UMCE49; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../sharedSample/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)"; GENERATE_INFOPLIST_FILE = YES; @@ -374,6 +371,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"iosSample/Preview Content\""; + DEVELOPMENT_TEAM = 4778UMCE49; ENABLE_PREVIEWS = YES; FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/../sharedSample/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)"; GENERATE_INFOPLIST_FILE = YES; diff --git a/sample/iosSample/iosSample/Input/VisualExtentions.swift b/sample/iosSample/iosSample/Input/VisualExtentions.swift index 7d370a9..2185c97 100644 --- a/sample/iosSample/iosSample/Input/VisualExtentions.swift +++ b/sample/iosSample/iosSample/Input/VisualExtentions.swift @@ -1,64 +1,76 @@ -import Foundation import sharedSample import SwiftUI extension KeyboardType { func toUI() -> UIKeyboardType { - let keyboardType: UIKeyboardType switch self { - case .text: keyboardType = .default - case .ascii: keyboardType = .asciiCapable - case .number: keyboardType = .numberPad - case .phone: keyboardType = .phonePad - case .uri: keyboardType = .URL - case .email: keyboardType = .emailAddress - case .password: keyboardType = .default - case .numberpassword: keyboardType = .numberPad - default: keyboardType = .default + case .ascii: + return .asciiCapable + case .number: + return .numberPad + case .phone: + return .phonePad + case .uri: + return .URL + case .email: + return .emailAddress + case .numberpassword: + return .numberPad + case .text, .password: + return .default + default: + return .default } - return keyboardType } } extension ImeAction { func toUI() -> SubmitLabel { - let label: SubmitLabel switch self { - case .default_: label = .done - case .done: label = .done - case .go: label = .go - case .search: label = .search - case .send: label = .send - case .previous: label = .route - case .next: label = .next - default: label = .done + case .done: + return .done + case .go: + return .go + case .search: + return .search + case .send: + return .send + case .previous: + return .route + case .next: + return .next + default: + return .done } - return label } } extension KeyboardCapitalization { - func toUI() -> TextInputAutocapitalization{ - let capitalization: TextInputAutocapitalization + func toUI() -> TextInputAutocapitalization { switch self { - case .none: capitalization = .never - case .characters: capitalization = .characters - case .sentences: capitalization = .sentences - case .words: capitalization = .words - default: capitalization = .never + case .characters: + return .characters + case .sentences: + return .sentences + case .words: + return .words + case .none: + return .never + default: + return .never } - return capitalization } } extension SubmitButtonState { func toUI() -> Color { - let color: Color switch self { - case SubmitButtonState.invalid: color = Color.red - case SubmitButtonState.valid: color = Color.green - default: color = Color.red + case SubmitButtonState.invalid: + return .red + case SubmitButtonState.valid: + return .green + default: + return .red } - return color } } diff --git a/sample/iosSample/iosSample/Input/VisualFormatter.swift b/sample/iosSample/iosSample/Input/VisualFormatter.swift index 8f4d115..1195654 100644 --- a/sample/iosSample/iosSample/Input/VisualFormatter.swift +++ b/sample/iosSample/iosSample/Input/VisualFormatter.swift @@ -1,11 +1,12 @@ import Foundation import sharedSample -class VisualFormatter: Formatter{ +class VisualFormatter: Formatter { let visualTransformation: VisualTransformation init(_ visualTransformation: VisualTransformation) { self.visualTransformation = visualTransformation + super.init() } @@ -23,9 +24,14 @@ class VisualFormatter: Formatter{ } } - override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer?) -> Bool { + override func getObjectValue( + _ obj: AutoreleasingUnsafeMutablePointer?, + for string: String, + errorDescription error: AutoreleasingUnsafeMutablePointer? + ) -> Bool { let result = visualTransformation.restore(text: string) obj?.pointee = result as AnyObject + return true } } diff --git a/sample/iosSample/iosSample/State/UnsafeObservableState.swift b/sample/iosSample/iosSample/State/UnsafeObservableState.swift index b347ba2..94b4eeb 100644 --- a/sample/iosSample/iosSample/State/UnsafeObservableState.swift +++ b/sample/iosSample/iosSample/State/UnsafeObservableState.swift @@ -2,18 +2,16 @@ import Foundation import sharedSample public class UnsafeObservableState: ObservableObject { - - @Published - var value: T? + @Published var value: T? private var cancelable: Cancelable? = nil init(_ state: Kotlinx_coroutines_coreStateFlow) { self.value = state.value as? T - cancelable = FlowWrapper(flow: state).collect(consumer: { value in + cancelable = FlowWrapper(flow: state).collect { value in self.value = value - }) + } } deinit { diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index 119d5e1..345fef2 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -2,15 +2,11 @@ import SwiftUI import sharedSample struct FormView: View { + @ObservedObject var submitButtonState: UnsafeObservableState + @ObservedObject var valid: UnsafeObservableState let formComponent: FormComponent - @ObservedObject - var submitButtonState: UnsafeObservableState - - @ObservedObject - var valid: UnsafeObservableState - init(formComponent: FormComponent) { self.formComponent = formComponent self.submitButtonState = UnsafeObservableState(formComponent.submitButtonState) @@ -21,27 +17,32 @@ struct FormView: View { VStack{ TextFieldWithControl( inputControl: formComponent.nameInput, - hint: MR.strings().name_hint.desc().localized() + hint: MR.strings().name_hint.desc().localized(), + isSecure: false ) TextFieldWithControl( inputControl: formComponent.emailInput, - hint: MR.strings().email_hint.desc().localized() + hint: MR.strings().email_hint.desc().localized(), + isSecure: false ) TextFieldWithControl( inputControl: formComponent.phoneInput, - hint: MR.strings().phone_hint.desc().localized() + hint: MR.strings().phone_hint.desc().localized(), + isSecure: false ) - SecureTextFieldWithControl( + TextFieldWithControl( inputControl: formComponent.passwordInput, - hint: MR.strings().password_hint.desc().localized() + hint: MR.strings().password_hint.desc().localized(), + isSecure: true ) - SecureTextFieldWithControl( + TextFieldWithControl( inputControl: formComponent.confirmPasswordInput, - hint: MR.strings().confirm_password_hint.desc().localized() + hint: MR.strings().confirm_password_hint.desc().localized(), + isSecure: true ) ToggleView( @@ -49,9 +50,9 @@ struct FormView: View { label: MR.strings().terms_hint.desc().localized() ) - SubmitButtonView( - buttonState: submitButtonState.value!, + SubmitButton( label: MR.strings().submit_button.desc().localized(), + buttonState: submitButtonState.value!, action: formComponent.onSubmitClicked ) diff --git a/sample/iosSample/iosSample/View/SecureTextFieldWithControl.swift b/sample/iosSample/iosSample/View/SecureTextFieldWithControl.swift deleted file mode 100644 index 35a00da..0000000 --- a/sample/iosSample/iosSample/View/SecureTextFieldWithControl.swift +++ /dev/null @@ -1,70 +0,0 @@ -import SwiftUI -import Combine -import sharedSample - -struct SecureTextFieldWithControl: View { - - private let hint: String - - private let inputControl: InputControl - - @ObservedObject - private var text: UnsafeObservableState - - @ObservedObject - private var error: UnsafeObservableState - - @ObservedObject - private var hasFocus: UnsafeObservableState - - @ObservedObject - private var enabled: UnsafeObservableState - - @State - private var keyboardOptions: KeyboardOptions - - @FocusState - private var isFocused: Bool - - init(inputControl: InputControl, hint: String) { - self.hint = hint - self.inputControl = inputControl - self.keyboardOptions = inputControl.keyboardOptions - self.text = UnsafeObservableState(inputControl.text) - self.error = UnsafeObservableState(inputControl.error) - self.hasFocus = UnsafeObservableState(inputControl.hasFocus) - self.enabled = UnsafeObservableState(inputControl.enabled) - } - - var body: some View { - VStack { - SecureField( - text: Binding { - String(text.value ?? "") - } set: { value in - inputControl.onTextChanged(text:value) - }, - prompt: Text(hint), - label: { - Text("123") - } - ) - .textFieldStyle(.roundedBorder) - .focused($isFocused) - .onChange(of: isFocused) { newValue in - inputControl.onFocusChanged(hasFocus: newValue) - } - .disabled(!(enabled.value?.boolValue ?? false)) - .keyboardType(keyboardOptions.keyboardType.toUI()) - .submitLabel(keyboardOptions.imeAction.toUI()) - .textInputAutocapitalization(keyboardOptions.capitalization.toUI()) - .autocorrectionDisabled(!keyboardOptions.autoCorrect) - - if let error = error.value { - Text(error.localized()) - .foregroundColor(.red) - } - } - .padding(4) - } -} diff --git a/sample/iosSample/iosSample/View/SubmitButtonView.swift b/sample/iosSample/iosSample/View/SubmitButtonView.swift index 0ba0dc0..3f2b5f6 100644 --- a/sample/iosSample/iosSample/View/SubmitButtonView.swift +++ b/sample/iosSample/iosSample/View/SubmitButtonView.swift @@ -1,23 +1,17 @@ import SwiftUI import sharedSample -struct SubmitButtonView: View { - - let buttonState: SubmitButtonState +struct SubmitButton: View { let label: String + let buttonState: SubmitButtonState let action: () -> Void - init(buttonState: SubmitButtonState, label: String, action: @escaping () -> Void) { - self.label = label - self.buttonState = buttonState - self.action = action - } - var body: some View { Button( action: action, label: { - Text(label).padding(8) + Text(label) + .padding(8) } ) .buttonStyle(BorderedButtonStyle()) diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index 8c4c1ad..407b05c 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -1,34 +1,23 @@ import SwiftUI -import Combine import sharedSample struct TextFieldWithControl: View { + @ObservedObject private var text: UnsafeObservableState + @ObservedObject private var error: UnsafeObservableState + @ObservedObject private var hasFocus: UnsafeObservableState + @ObservedObject private var enabled: UnsafeObservableState - private let hint: String + @State private var keyboardOptions: KeyboardOptions + @FocusState private var isFocused: Bool + private let hint: String private let inputControl: InputControl - - @ObservedObject - private var text: UnsafeObservableState + private let isSecure: Bool - @ObservedObject - private var error: UnsafeObservableState - - @ObservedObject - private var hasFocus: UnsafeObservableState - - @ObservedObject - private var enabled: UnsafeObservableState - - @State - private var keyboardOptions: KeyboardOptions - - @FocusState - private var isFocused: Bool - - init(inputControl: InputControl, hint: String) { + init(inputControl: InputControl, hint: String, isSecure: Bool) { self.hint = hint self.inputControl = inputControl + self.isSecure = isSecure self.keyboardOptions = inputControl.keyboardOptions self.text = UnsafeObservableState(inputControl.text) self.error = UnsafeObservableState(inputControl.error) @@ -38,25 +27,25 @@ struct TextFieldWithControl: View { var body: some View { VStack { - TextField( - hint, - value: Binding { + TextFieldView( + text: Binding { String(text.value ?? "") } set: { value in - inputControl.onTextChanged(text:value) + inputControl.onTextChanged(text: value) }, + isSecure: isSecure, + hint: hint, formatter: VisualFormatter(inputControl.visualTransformation) ) - .textFieldStyle(.roundedBorder) - .focused($isFocused) - .onChange(of: isFocused) { newValue in - inputControl.onFocusChanged(hasFocus: newValue) - } .disabled(!(enabled.value?.boolValue ?? false)) .keyboardType(keyboardOptions.keyboardType.toUI()) .submitLabel(keyboardOptions.imeAction.toUI()) .textInputAutocapitalization(keyboardOptions.capitalization.toUI()) .autocorrectionDisabled(!keyboardOptions.autoCorrect) + .focused($isFocused) + .onChange(of: isFocused) { newValue in + inputControl.onFocusChanged(hasFocus: newValue) + } if let error = error.value { Text(error.localized()) @@ -65,4 +54,32 @@ struct TextFieldWithControl: View { } .padding(4) } + + private struct TextFieldView: View { + @Binding var text: String + + let isSecure: Bool + let hint: String + let formatter: Formatter + + var body: some View { + if isSecure { + SecureField( + text: $text, + prompt: Text(hint), + label: { + Text("") + } + ) + .textFieldStyle(.roundedBorder) + } else { + TextField( + hint, + value: $text, + formatter: formatter + ) + .textFieldStyle(.roundedBorder) + } + } + } } diff --git a/sample/iosSample/iosSample/View/ToggleView.swift b/sample/iosSample/iosSample/View/ToggleView.swift index 5fb91af..6779a20 100644 --- a/sample/iosSample/iosSample/View/ToggleView.swift +++ b/sample/iosSample/iosSample/View/ToggleView.swift @@ -2,17 +2,12 @@ import SwiftUI import sharedSample struct ToggleView: View { + @ObservedObject var checked: UnsafeObservableState + @ObservedObject private var error: UnsafeObservableState let checkControl: CheckControl - let label: String - @ObservedObject - var checked: UnsafeObservableState - - @ObservedObject - private var error: UnsafeObservableState - init(checkControl: CheckControl, label: String) { self.label = label self.checkControl = checkControl diff --git a/sample/iosSample/iosSample/iosSampleApp.swift b/sample/iosSample/iosSample/iosSampleApp.swift index fb7cf94..3ca73f2 100644 --- a/sample/iosSample/iosSample/iosSampleApp.swift +++ b/sample/iosSample/iosSample/iosSampleApp.swift @@ -3,9 +3,7 @@ import sharedSample @main struct iosSampleApp: App { - - @StateObject - private var rootHolder = RootHolder() + @StateObject private var rootHolder = RootHolder() var body: some Scene { WindowGroup { From f521e0b3aef32e48a0b021434e1b6da5546c9a94 Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Thu, 9 Feb 2023 17:38:56 +0300 Subject: [PATCH 02/11] add scroll to field --- .../iosSample/iosSample/View/FormView.swift | 197 +++++++++++++----- .../iosSample/View/TextFieldWithControl.swift | 4 +- 2 files changed, 152 insertions(+), 49 deletions(-) diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index 345fef2..fe1f45c 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -2,10 +2,21 @@ import SwiftUI import sharedSample struct FormView: View { - @ObservedObject var submitButtonState: UnsafeObservableState - @ObservedObject var valid: UnsafeObservableState - + private enum Field: Int { + + case name = 0 + case email = 1 + case phone = 2 + case password = 3 + case passwordConfirmation = 4 + } + let formComponent: FormComponent + + @ObservedObject private var submitButtonState: UnsafeObservableState + @ObservedObject private var valid: UnsafeObservableState + + @FocusState private var focus: Int? init(formComponent: FormComponent) { self.formComponent = formComponent @@ -14,51 +25,88 @@ struct FormView: View { } var body: some View { - VStack{ - TextFieldWithControl( - inputControl: formComponent.nameInput, - hint: MR.strings().name_hint.desc().localized(), - isSecure: false - ) - - TextFieldWithControl( - inputControl: formComponent.emailInput, - hint: MR.strings().email_hint.desc().localized(), - isSecure: false - ) - - TextFieldWithControl( - inputControl: formComponent.phoneInput, - hint: MR.strings().phone_hint.desc().localized(), - isSecure: false - ) - - TextFieldWithControl( - inputControl: formComponent.passwordInput, - hint: MR.strings().password_hint.desc().localized(), - isSecure: true - ) - - TextFieldWithControl( - inputControl: formComponent.confirmPasswordInput, - hint: MR.strings().confirm_password_hint.desc().localized(), - isSecure: true - ) - - ToggleView( - checkControl: formComponent.termsCheckBox, - label: MR.strings().terms_hint.desc().localized() - ) - - SubmitButton( - label: MR.strings().submit_button.desc().localized(), - buttonState: submitButtonState.value!, - action: formComponent.onSubmitClicked - ) - - if(valid.value?.boolValue ?? false) { - Text(MR.strings().success_message.desc().localized()) - .padding(8) + ScrollView(showsIndicators: false) { + ScrollViewReader { proxy in + VStack{ + TextFieldWithControl( + inputControl: formComponent.nameInput, + hint: MR.strings().name_hint.desc().localized(), + isSecure: false + ) + .scrolledFocus( + focus: _focus, + id: Field.name.rawValue, + nextId: Field.email.rawValue, + proxy: proxy + ) + .id(Field.name.rawValue) + TextFieldWithControl( + inputControl: formComponent.emailInput, + hint: MR.strings().email_hint.desc().localized(), + isSecure: false + ) + .scrolledFocus( + focus: _focus, + id: Field.email.rawValue, + nextId: Field.phone.rawValue, + proxy: proxy + ) + .id(Field.email.rawValue) + TextFieldWithControl( + inputControl: formComponent.phoneInput, + hint: MR.strings().phone_hint.desc().localized(), + isSecure: false + ) + .scrolledFocus( + focus: _focus, + id: Field.phone.rawValue, + nextId: Field.password.rawValue, + proxy: proxy + ) + .id(Field.phone.rawValue) + TextFieldWithControl( + inputControl: formComponent.passwordInput, + hint: MR.strings().password_hint.desc().localized(), + isSecure: true + ) + .scrolledFocus( + focus: _focus, + id: Field.password.rawValue, + nextId: Field.passwordConfirmation.rawValue, + proxy: proxy + ) + .id(Field.password.rawValue) + TextFieldWithControl( + inputControl: formComponent.confirmPasswordInput, + hint: MR.strings().confirm_password_hint.desc().localized(), + isSecure: true + ) + .scrolledFocus( + focus: _focus, + id: Field.passwordConfirmation.rawValue, + nextId: 5, + proxy: proxy + ) + .id(Field.passwordConfirmation.rawValue) + ToggleView( + checkControl: formComponent.termsCheckBox, + label: MR.strings().terms_hint.desc().localized() + ) + .id(5) + + SubmitButton( + label: MR.strings().submit_button.desc().localized(), + buttonState: submitButtonState.value!, + action: formComponent.onSubmitClicked + ) + + if(valid.value?.boolValue ?? false) { + Text(MR.strings().success_message.desc().localized()) + .padding(8) + } + + Color.clear.padding(.bottom, 70) + } } } } @@ -70,3 +118,56 @@ struct FormView_Previews: PreviewProvider { FormView(formComponent: FakeFormComponent()) } } + +private struct ScrolledFocusField: ViewModifier { + + @FocusState var focus: Int? + + let id: Int + let nextId: Int + let proxy: ScrollViewProxy + let anchor: UnitPoint + let nextAnchor: UnitPoint + + func body(content: Content) -> some View { + content + .focused($focus, equals: id) + .onTapGesture { + scrollToRowWithAnimation(proxy: proxy, id, anchor: anchor) + } + .onSubmit { + scrollToRowWithAnimation(proxy: proxy, nextId, anchor: nextAnchor) + focus = nextId + } + } +} + +private extension ViewModifier { + + func scrollToRowWithAnimation(proxy: ScrollViewProxy, _ row: Int, anchor: UnitPoint = .center) { + withAnimation { + proxy.scrollTo(row, anchor: anchor) + } + } +} + +extension View { + + func scrolledFocus( + focus: FocusState, + id: Int, + nextId: Int, + proxy: ScrollViewProxy, + anchor: UnitPoint = .center, + nextAnchor: UnitPoint = .center + ) -> some View { + modifier(ScrolledFocusField( + focus: focus, + id: id, + nextId: nextId, + proxy: proxy, + anchor: anchor, + nextAnchor: nextAnchor + )) + } +} diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index 407b05c..f72b428 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -52,7 +52,8 @@ struct TextFieldWithControl: View { .foregroundColor(.red) } } - .padding(4) + .padding(.horizontal, 4) + .padding(.vertical, 40) } private struct TextFieldView: View { @@ -71,6 +72,7 @@ struct TextFieldWithControl: View { Text("") } ) + .textContentType(.password) .textFieldStyle(.roundedBorder) } else { TextField( From cdaff2cb70c7d22fbd8986afd19e7b5e23d29e57 Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Thu, 9 Feb 2023 19:10:58 +0300 Subject: [PATCH 03/11] remove formatter --- .../iosSample/View/TextFieldWithControl.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index f72b428..ad1f3af 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -32,10 +32,10 @@ struct TextFieldWithControl: View { String(text.value ?? "") } set: { value in inputControl.onTextChanged(text: value) + text.value = text.value }, isSecure: isSecure, - hint: hint, - formatter: VisualFormatter(inputControl.visualTransformation) + hint: hint ) .disabled(!(enabled.value?.boolValue ?? false)) .keyboardType(keyboardOptions.keyboardType.toUI()) @@ -61,7 +61,6 @@ struct TextFieldWithControl: View { let isSecure: Bool let hint: String - let formatter: Formatter var body: some View { if isSecure { @@ -75,11 +74,7 @@ struct TextFieldWithControl: View { .textContentType(.password) .textFieldStyle(.roundedBorder) } else { - TextField( - hint, - value: $text, - formatter: formatter - ) + TextField(hint, text: $text) .textFieldStyle(.roundedBorder) } } From d123e57da13b3a78c7279feb9a3cf53c5939a7a1 Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Thu, 9 Feb 2023 20:14:37 +0300 Subject: [PATCH 04/11] refactor+bufgix Make refactor Fix bug with no scroll Fix bug with no scroll-to-field Fix bug no focus after submit --- .../iosSample.xcodeproj/project.pbxproj | 54 +++++++++++----- .../{ => Application}/iosSampleApp.swift | 2 +- .../iosSample/Input/VisualFormatter.swift | 37 ----------- .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../State/UnsafeObservableState.swift | 2 +- .../Extensions}/VisualExtentions.swift | 0 .../iosSample/iosSample/View/FormView.swift | 57 +---------------- .../iosSample/View/TextFieldWithControl.swift | 8 ++- .../iosSample/iosSample/View/ToggleView.swift | 6 +- .../ViewModifiers/ScrolledFocusField.swift | 61 +++++++++++++++++++ 12 files changed, 113 insertions(+), 114 deletions(-) rename sample/iosSample/iosSample/{ => Application}/iosSampleApp.swift (94%) delete mode 100644 sample/iosSample/iosSample/Input/VisualFormatter.swift rename sample/iosSample/iosSample/{ => Resources}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename sample/iosSample/iosSample/{ => Resources}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename sample/iosSample/iosSample/{ => Resources}/Assets.xcassets/Contents.json (100%) rename sample/iosSample/iosSample/{Input => View/Extensions}/VisualExtentions.swift (100%) create mode 100644 sample/iosSample/iosSample/View/ViewModifiers/ScrolledFocusField.swift diff --git a/sample/iosSample/iosSample.xcodeproj/project.pbxproj b/sample/iosSample/iosSample.xcodeproj/project.pbxproj index 94b6d8a..bd7cb62 100644 --- a/sample/iosSample/iosSample.xcodeproj/project.pbxproj +++ b/sample/iosSample/iosSample.xcodeproj/project.pbxproj @@ -7,12 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + E521689129955B3F00F366EF /* ScrolledFocusField.swift in Sources */ = {isa = PBXBuildFile; fileRef = E521689029955B3F00F366EF /* ScrolledFocusField.swift */; }; EC7C1FEA2987B251001DCE8B /* iosSampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C1FE92987B251001DCE8B /* iosSampleApp.swift */; }; EC7C1FEC2987B251001DCE8B /* TextFieldWithControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C1FEB2987B251001DCE8B /* TextFieldWithControl.swift */; }; EC7C1FEE2987B252001DCE8B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7C1FED2987B252001DCE8B /* Assets.xcassets */; }; EC7C1FF12987B252001DCE8B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EC7C1FF02987B252001DCE8B /* Preview Assets.xcassets */; }; EC7C20172987F483001DCE8B /* UnsafeObservableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C20162987F483001DCE8B /* UnsafeObservableState.swift */; }; - EC7C201B298A6A67001DCE8B /* VisualFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C201A298A6A66001DCE8B /* VisualFormatter.swift */; }; EC7C201E298A6AD1001DCE8B /* VisualExtentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C201D298A6AD1001DCE8B /* VisualExtentions.swift */; }; EC7C2023298A710B001DCE8B /* ToggleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C2022298A710B001DCE8B /* ToggleView.swift */; }; EC7C2025298A7883001DCE8B /* SubmitButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC7C2024298A7883001DCE8B /* SubmitButtonView.swift */; }; @@ -20,13 +20,13 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + E521689029955B3F00F366EF /* ScrolledFocusField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrolledFocusField.swift; sourceTree = ""; }; EC7C1FE62987B251001DCE8B /* iosSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; EC7C1FE92987B251001DCE8B /* iosSampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iosSampleApp.swift; sourceTree = ""; }; EC7C1FEB2987B251001DCE8B /* TextFieldWithControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextFieldWithControl.swift; sourceTree = ""; }; EC7C1FED2987B252001DCE8B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; EC7C1FF02987B252001DCE8B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; EC7C20162987F483001DCE8B /* UnsafeObservableState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsafeObservableState.swift; sourceTree = ""; }; - EC7C201A298A6A66001DCE8B /* VisualFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualFormatter.swift; sourceTree = ""; }; EC7C201D298A6AD1001DCE8B /* VisualExtentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualExtentions.swift; sourceTree = ""; }; EC7C2022298A710B001DCE8B /* ToggleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleView.swift; sourceTree = ""; }; EC7C2024298A7883001DCE8B /* SubmitButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubmitButtonView.swift; sourceTree = ""; }; @@ -44,6 +44,38 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + E521688F29955B0E00F366EF /* ViewModifiers */ = { + isa = PBXGroup; + children = ( + E521689029955B3F00F366EF /* ScrolledFocusField.swift */, + ); + path = ViewModifiers; + sourceTree = ""; + }; + E521689429955E2100F366EF /* Extensions */ = { + isa = PBXGroup; + children = ( + EC7C201D298A6AD1001DCE8B /* VisualExtentions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + E521689529955E5600F366EF /* Application */ = { + isa = PBXGroup; + children = ( + EC7C1FE92987B251001DCE8B /* iosSampleApp.swift */, + ); + path = Application; + sourceTree = ""; + }; + E521689629955E6800F366EF /* Resources */ = { + isa = PBXGroup; + children = ( + EC7C1FED2987B252001DCE8B /* Assets.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; EC7C1FDD2987B251001DCE8B = { isa = PBXGroup; children = ( @@ -64,10 +96,9 @@ isa = PBXGroup; children = ( EC7C2021298A70E7001DCE8B /* View */, - EC7C201C298A6AB4001DCE8B /* Input */, EC7C20152987F458001DCE8B /* State */, - EC7C1FE92987B251001DCE8B /* iosSampleApp.swift */, - EC7C1FED2987B252001DCE8B /* Assets.xcassets */, + E521689529955E5600F366EF /* Application */, + E521689629955E6800F366EF /* Resources */, EC7C1FEF2987B252001DCE8B /* Preview Content */, ); path = iosSample; @@ -89,18 +120,11 @@ path = State; sourceTree = ""; }; - EC7C201C298A6AB4001DCE8B /* Input */ = { - isa = PBXGroup; - children = ( - EC7C201A298A6A66001DCE8B /* VisualFormatter.swift */, - EC7C201D298A6AD1001DCE8B /* VisualExtentions.swift */, - ); - path = Input; - sourceTree = ""; - }; EC7C2021298A70E7001DCE8B /* View */ = { isa = PBXGroup; children = ( + E521689429955E2100F366EF /* Extensions */, + E521688F29955B0E00F366EF /* ViewModifiers */, EC7C1FEB2987B251001DCE8B /* TextFieldWithControl.swift */, EC7C2022298A710B001DCE8B /* ToggleView.swift */, EC7C2024298A7883001DCE8B /* SubmitButtonView.swift */, @@ -201,12 +225,12 @@ buildActionMask = 2147483647; files = ( EC7C2023298A710B001DCE8B /* ToggleView.swift in Sources */, - EC7C201B298A6A67001DCE8B /* VisualFormatter.swift in Sources */, EC7C1FEC2987B251001DCE8B /* TextFieldWithControl.swift in Sources */, EC7C2025298A7883001DCE8B /* SubmitButtonView.swift in Sources */, EC7C20172987F483001DCE8B /* UnsafeObservableState.swift in Sources */, ECB57B3B298CFC4E00AE547C /* FormView.swift in Sources */, EC7C201E298A6AD1001DCE8B /* VisualExtentions.swift in Sources */, + E521689129955B3F00F366EF /* ScrolledFocusField.swift in Sources */, EC7C1FEA2987B251001DCE8B /* iosSampleApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/sample/iosSample/iosSample/iosSampleApp.swift b/sample/iosSample/iosSample/Application/iosSampleApp.swift similarity index 94% rename from sample/iosSample/iosSample/iosSampleApp.swift rename to sample/iosSample/iosSample/Application/iosSampleApp.swift index 3ca73f2..9643c92 100644 --- a/sample/iosSample/iosSample/iosSampleApp.swift +++ b/sample/iosSample/iosSample/Application/iosSampleApp.swift @@ -14,7 +14,7 @@ struct iosSampleApp: App { } } -private class RootHolder : ObservableObject { +private final class RootHolder : ObservableObject { let lifecycle: LifecycleRegistry let formComponent: FormComponent diff --git a/sample/iosSample/iosSample/Input/VisualFormatter.swift b/sample/iosSample/iosSample/Input/VisualFormatter.swift deleted file mode 100644 index 1195654..0000000 --- a/sample/iosSample/iosSample/Input/VisualFormatter.swift +++ /dev/null @@ -1,37 +0,0 @@ -import Foundation -import sharedSample - -class VisualFormatter: Formatter { - let visualTransformation: VisualTransformation - - init(_ visualTransformation: VisualTransformation) { - self.visualTransformation = visualTransformation - - super.init() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func string(for obj: Any?) -> String? { - let string = obj as? String - - if let string = string { - return visualTransformation.filter(text: string).text - } else { - return nil - } - } - - override func getObjectValue( - _ obj: AutoreleasingUnsafeMutablePointer?, - for string: String, - errorDescription error: AutoreleasingUnsafeMutablePointer? - ) -> Bool { - let result = visualTransformation.restore(text: string) - obj?.pointee = result as AnyObject - - return true - } -} diff --git a/sample/iosSample/iosSample/Assets.xcassets/AccentColor.colorset/Contents.json b/sample/iosSample/iosSample/Resources/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from sample/iosSample/iosSample/Assets.xcassets/AccentColor.colorset/Contents.json rename to sample/iosSample/iosSample/Resources/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/sample/iosSample/iosSample/Assets.xcassets/AppIcon.appiconset/Contents.json b/sample/iosSample/iosSample/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from sample/iosSample/iosSample/Assets.xcassets/AppIcon.appiconset/Contents.json rename to sample/iosSample/iosSample/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/sample/iosSample/iosSample/Assets.xcassets/Contents.json b/sample/iosSample/iosSample/Resources/Assets.xcassets/Contents.json similarity index 100% rename from sample/iosSample/iosSample/Assets.xcassets/Contents.json rename to sample/iosSample/iosSample/Resources/Assets.xcassets/Contents.json diff --git a/sample/iosSample/iosSample/State/UnsafeObservableState.swift b/sample/iosSample/iosSample/State/UnsafeObservableState.swift index 94b4eeb..1c026f3 100644 --- a/sample/iosSample/iosSample/State/UnsafeObservableState.swift +++ b/sample/iosSample/iosSample/State/UnsafeObservableState.swift @@ -1,7 +1,7 @@ import Foundation import sharedSample -public class UnsafeObservableState: ObservableObject { +public final class UnsafeObservableState: ObservableObject { @Published var value: T? private var cancelable: Cancelable? = nil diff --git a/sample/iosSample/iosSample/Input/VisualExtentions.swift b/sample/iosSample/iosSample/View/Extensions/VisualExtentions.swift similarity index 100% rename from sample/iosSample/iosSample/Input/VisualExtentions.swift rename to sample/iosSample/iosSample/View/Extensions/VisualExtentions.swift diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index fe1f45c..f6be8f5 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -11,13 +11,13 @@ struct FormView: View { case passwordConfirmation = 4 } - let formComponent: FormComponent - @ObservedObject private var submitButtonState: UnsafeObservableState @ObservedObject private var valid: UnsafeObservableState @FocusState private var focus: Int? + private let formComponent: FormComponent + init(formComponent: FormComponent) { self.formComponent = formComponent self.submitButtonState = UnsafeObservableState(formComponent.submitButtonState) @@ -118,56 +118,3 @@ struct FormView_Previews: PreviewProvider { FormView(formComponent: FakeFormComponent()) } } - -private struct ScrolledFocusField: ViewModifier { - - @FocusState var focus: Int? - - let id: Int - let nextId: Int - let proxy: ScrollViewProxy - let anchor: UnitPoint - let nextAnchor: UnitPoint - - func body(content: Content) -> some View { - content - .focused($focus, equals: id) - .onTapGesture { - scrollToRowWithAnimation(proxy: proxy, id, anchor: anchor) - } - .onSubmit { - scrollToRowWithAnimation(proxy: proxy, nextId, anchor: nextAnchor) - focus = nextId - } - } -} - -private extension ViewModifier { - - func scrollToRowWithAnimation(proxy: ScrollViewProxy, _ row: Int, anchor: UnitPoint = .center) { - withAnimation { - proxy.scrollTo(row, anchor: anchor) - } - } -} - -extension View { - - func scrolledFocus( - focus: FocusState, - id: Int, - nextId: Int, - proxy: ScrollViewProxy, - anchor: UnitPoint = .center, - nextAnchor: UnitPoint = .center - ) -> some View { - modifier(ScrolledFocusField( - focus: focus, - id: id, - nextId: nextId, - proxy: proxy, - anchor: anchor, - nextAnchor: nextAnchor - )) - } -} diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index ad1f3af..c0c49d1 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -23,6 +23,8 @@ struct TextFieldWithControl: View { self.error = UnsafeObservableState(inputControl.error) self.hasFocus = UnsafeObservableState(inputControl.hasFocus) self.enabled = UnsafeObservableState(inputControl.enabled) + + isFocused = hasFocus.value?.boolValue ?? false } var body: some View { @@ -46,14 +48,16 @@ struct TextFieldWithControl: View { .onChange(of: isFocused) { newValue in inputControl.onFocusChanged(hasFocus: newValue) } + .onChange(of: hasFocus.value?.boolValue ?? false) { newValue in + isFocused = hasFocus.value?.boolValue ?? false + } if let error = error.value { Text(error.localized()) .foregroundColor(.red) } } - .padding(.horizontal, 4) - .padding(.vertical, 40) + .padding(20) } private struct TextFieldView: View { diff --git a/sample/iosSample/iosSample/View/ToggleView.swift b/sample/iosSample/iosSample/View/ToggleView.swift index 6779a20..b1acb5a 100644 --- a/sample/iosSample/iosSample/View/ToggleView.swift +++ b/sample/iosSample/iosSample/View/ToggleView.swift @@ -2,11 +2,11 @@ import SwiftUI import sharedSample struct ToggleView: View { - @ObservedObject var checked: UnsafeObservableState + @ObservedObject private var checked: UnsafeObservableState @ObservedObject private var error: UnsafeObservableState - let checkControl: CheckControl - let label: String + private let checkControl: CheckControl + private let label: String init(checkControl: CheckControl, label: String) { self.label = label diff --git a/sample/iosSample/iosSample/View/ViewModifiers/ScrolledFocusField.swift b/sample/iosSample/iosSample/View/ViewModifiers/ScrolledFocusField.swift new file mode 100644 index 0000000..9005443 --- /dev/null +++ b/sample/iosSample/iosSample/View/ViewModifiers/ScrolledFocusField.swift @@ -0,0 +1,61 @@ +// +// ScrolledFocusField.swift +// iosSample +// +// Created by Чаусов Николай on 09.02.2023. +// + +import SwiftUI + +private struct ScrolledFocusField: ViewModifier { + + @FocusState var focus: Int? + + let id: Int + let nextId: Int + let proxy: ScrollViewProxy + let anchor: UnitPoint + let nextAnchor: UnitPoint + + func body(content: Content) -> some View { + content + .focused($focus, equals: id) + .onTapGesture { + scrollToRowWithAnimation(proxy: proxy, id, anchor: anchor) + } + .onSubmit { + scrollToRowWithAnimation(proxy: proxy, nextId, anchor: nextAnchor) + focus = nextId + } + } +} + +private extension ViewModifier { + + func scrollToRowWithAnimation(proxy: ScrollViewProxy, _ row: Int, anchor: UnitPoint = .center) { + withAnimation { + proxy.scrollTo(row, anchor: anchor) + } + } +} + +extension View { + + func scrolledFocus( + focus: FocusState, + id: Int, + nextId: Int, + proxy: ScrollViewProxy, + anchor: UnitPoint = .center, + nextAnchor: UnitPoint = .center + ) -> some View { + modifier(ScrolledFocusField( + focus: focus, + id: id, + nextId: nextId, + proxy: proxy, + anchor: anchor, + nextAnchor: nextAnchor + )) + } +} From 50d8e005500fb0371a9fa145d74904867447ac22 Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Fri, 10 Feb 2023 09:45:25 +0300 Subject: [PATCH 05/11] rename vars swiftly --- sample/iosSample/iosSample/View/FormView.swift | 6 +++--- .../iosSample/iosSample/View/TextFieldWithControl.swift | 8 ++++---- sample/iosSample/iosSample/View/ToggleView.swift | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index f6be8f5..e9b4f73 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -12,7 +12,7 @@ struct FormView: View { } @ObservedObject private var submitButtonState: UnsafeObservableState - @ObservedObject private var valid: UnsafeObservableState + @ObservedObject private var isValid: UnsafeObservableState @FocusState private var focus: Int? @@ -21,7 +21,7 @@ struct FormView: View { init(formComponent: FormComponent) { self.formComponent = formComponent self.submitButtonState = UnsafeObservableState(formComponent.submitButtonState) - self.valid = UnsafeObservableState(formComponent.valid) + self.isValid = UnsafeObservableState(formComponent.valid) } var body: some View { @@ -100,7 +100,7 @@ struct FormView: View { action: formComponent.onSubmitClicked ) - if(valid.value?.boolValue ?? false) { + if(isValid.value?.boolValue ?? false) { Text(MR.strings().success_message.desc().localized()) .padding(8) } diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index c0c49d1..dbda534 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -5,13 +5,13 @@ struct TextFieldWithControl: View { @ObservedObject private var text: UnsafeObservableState @ObservedObject private var error: UnsafeObservableState @ObservedObject private var hasFocus: UnsafeObservableState - @ObservedObject private var enabled: UnsafeObservableState + @ObservedObject private var isEnabled: UnsafeObservableState @State private var keyboardOptions: KeyboardOptions @FocusState private var isFocused: Bool - private let hint: String private let inputControl: InputControl + private let hint: String private let isSecure: Bool init(inputControl: InputControl, hint: String, isSecure: Bool) { @@ -22,7 +22,7 @@ struct TextFieldWithControl: View { self.text = UnsafeObservableState(inputControl.text) self.error = UnsafeObservableState(inputControl.error) self.hasFocus = UnsafeObservableState(inputControl.hasFocus) - self.enabled = UnsafeObservableState(inputControl.enabled) + self.isEnabled = UnsafeObservableState(inputControl.enabled) isFocused = hasFocus.value?.boolValue ?? false } @@ -39,7 +39,7 @@ struct TextFieldWithControl: View { isSecure: isSecure, hint: hint ) - .disabled(!(enabled.value?.boolValue ?? false)) + .disabled(!(isEnabled.value?.boolValue ?? false)) .keyboardType(keyboardOptions.keyboardType.toUI()) .submitLabel(keyboardOptions.imeAction.toUI()) .textInputAutocapitalization(keyboardOptions.capitalization.toUI()) diff --git a/sample/iosSample/iosSample/View/ToggleView.swift b/sample/iosSample/iosSample/View/ToggleView.swift index b1acb5a..565a199 100644 --- a/sample/iosSample/iosSample/View/ToggleView.swift +++ b/sample/iosSample/iosSample/View/ToggleView.swift @@ -2,7 +2,7 @@ import SwiftUI import sharedSample struct ToggleView: View { - @ObservedObject private var checked: UnsafeObservableState + @ObservedObject private var isChecked: UnsafeObservableState @ObservedObject private var error: UnsafeObservableState private let checkControl: CheckControl @@ -11,7 +11,7 @@ struct ToggleView: View { init(checkControl: CheckControl, label: String) { self.label = label self.checkControl = checkControl - checked = UnsafeObservableState(checkControl.checked) + isChecked = UnsafeObservableState(checkControl.checked) error = UnsafeObservableState(checkControl.error) } @@ -19,7 +19,7 @@ struct ToggleView: View { VStack { Toggle( isOn: Binding( - get: { checked.value?.boolValue ?? false }, + get: { isChecked.value?.boolValue ?? false }, set: checkControl.onCheckedChanged ), label: { From 64f7e1f960ef6a5c6e371c2a1f9fac98615a069a Mon Sep 17 00:00:00 2001 From: Artur Artikov Date: Thu, 9 Feb 2023 17:16:16 +0300 Subject: [PATCH 06/11] Clean up Android and shared code --- .../control/InputControl.kt | 2 +- .../options/VisualTransformation.kt | 19 ------------------- .../ui/widgets/PasswordTextField.kt | 9 +++++---- .../android_sample/ui/widgets/TextField.kt | 3 ++- .../sharedsample/ui/RealFormComponent.kt | 13 +++++++------ .../sharedsample/utils/FlowWrapper.kt | 10 ---------- 6 files changed, 15 insertions(+), 41 deletions(-) diff --git a/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/control/InputControl.kt b/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/control/InputControl.kt index f8c4d58..d3384fc 100644 --- a/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/control/InputControl.kt +++ b/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/control/InputControl.kt @@ -22,7 +22,7 @@ class InputControl( val maxLength: Int = Int.MAX_VALUE, val keyboardOptions: KeyboardOptions, val textTransformation: TextTransformation? = null, - val visualTransformation: VisualTransformation = VisualTransformation.Companion.None + val visualTransformation: VisualTransformation = VisualTransformation.None ) : ValidatableControl { constructor(coroutineScope: CoroutineScope) : this( diff --git a/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/options/VisualTransformation.kt b/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/options/VisualTransformation.kt index f6fbfd5..4e615e5 100644 --- a/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/options/VisualTransformation.kt +++ b/kmm-form-validation/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/options/VisualTransformation.kt @@ -29,23 +29,4 @@ interface VisualTransformation { } fun restore(text: String): String -} - -/** - * The Visual Filter can be used for password Input Field. - * - * Note that this visual filter only works for ASCII characters. - * - * @param mask The mask character used instead of original text. - */ -data class PasswordVisualTransformation(val mask: Char = '\u2022') : VisualTransformation { - constructor() : this(mask = '\u2022') - - override fun filter(text: String): TransformedText { - return TransformedText(mask.toString().repeat(text.length), OffsetMapping.Identity) - } - - override fun restore(text: String): String { - return text - } } \ No newline at end of file diff --git a/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/PasswordTextField.kt b/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/PasswordTextField.kt index c63394b..3584107 100644 --- a/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/PasswordTextField.kt +++ b/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/PasswordTextField.kt @@ -15,16 +15,17 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.input.PasswordVisualTransformation import dev.icerock.moko.resources.compose.localized import kotlinx.coroutines.flow.collectLatest import ru.mobileup.kmm_form_validation.android_sample.R +import ru.mobileup.kmm_form_validation.control.InputControl import ru.mobileup.kmm_form_validation.toCompose @OptIn(ExperimentalFoundationApi::class) @Composable fun PasswordTextField( - inputControl: ru.mobileup.kmm_form_validation.control.InputControl, + inputControl: InputControl, label: String, modifier: Modifier = Modifier ) { @@ -64,9 +65,9 @@ fun PasswordTextField( isError = error != null, onValueChange = inputControl::onTextChanged, visualTransformation = if (passwordVisibility) { - VisualTransformation.None - } else { inputControl.visualTransformation.toCompose() + } else { + PasswordVisualTransformation() }, trailingIcon = { val image = if (passwordVisibility) { diff --git a/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/TextField.kt b/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/TextField.kt index 5545e0d..874713f 100644 --- a/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/TextField.kt +++ b/sample/androidSample/src/main/java/ru/mobileup/kmm_form_validation/android_sample/ui/widgets/TextField.kt @@ -14,12 +14,13 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged import dev.icerock.moko.resources.compose.localized import kotlinx.coroutines.flow.collectLatest +import ru.mobileup.kmm_form_validation.control.InputControl import ru.mobileup.kmm_form_validation.toCompose @OptIn(ExperimentalFoundationApi::class) @Composable fun TextField( - inputControl: ru.mobileup.kmm_form_validation.control.InputControl, + inputControl: InputControl, label: String, modifier: Modifier = Modifier ) { diff --git a/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/ui/RealFormComponent.kt b/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/ui/RealFormComponent.kt index ad9df6e..12479d4 100644 --- a/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/ui/RealFormComponent.kt +++ b/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/ui/RealFormComponent.kt @@ -6,7 +6,10 @@ import dev.icerock.moko.resources.desc.StringDesc import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import ru.mobileup.kmm_form_validation.options.* +import ru.mobileup.kmm_form_validation.options.ImeAction +import ru.mobileup.kmm_form_validation.options.KeyboardCapitalization +import ru.mobileup.kmm_form_validation.options.KeyboardOptions +import ru.mobileup.kmm_form_validation.options.KeyboardType import ru.mobileup.kmm_form_validation.sharedsample.MR import ru.mobileup.kmm_form_validation.sharedsample.utils.* import ru.mobileup.kmm_form_validation.validation.control.* @@ -57,7 +60,7 @@ class RealFormComponent( keyboardType = KeyboardType.Phone, imeAction = ImeAction.Next ), - textTransformation = { it.replace(Regex("[^1234567890(-)+]"), "") }, + textTransformation = { text -> text.filter { it.isDigit() } }, visualTransformation = RussianPhoneNumberVisualTransformation ) @@ -65,16 +68,14 @@ class RealFormComponent( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, imeAction = ImeAction.Next - ), - visualTransformation = PasswordVisualTransformation() + ) ) override val confirmPasswordInput = InputControl( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Password, imeAction = ImeAction.Done - ), - visualTransformation = PasswordVisualTransformation() + ) ) override val termsCheckBox = CheckControl() diff --git a/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/utils/FlowWrapper.kt b/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/utils/FlowWrapper.kt index 226dabf..70ad29f 100644 --- a/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/utils/FlowWrapper.kt +++ b/sample/sharedSample/src/commonMain/kotlin/ru/mobileup/kmm_form_validation/sharedsample/utils/FlowWrapper.kt @@ -4,7 +4,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -29,13 +28,4 @@ open class FlowWrapper( } } } -} - -class MutableStateFlowWrapper( - private val stateFlow: MutableStateFlow -) : FlowWrapper(stateFlow) { - - fun update(value: T) { - stateFlow.value = value - } } \ No newline at end of file From a7bf1318fdf565baf2653311f399942ee8050bde Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Fri, 10 Feb 2023 14:59:05 +0300 Subject: [PATCH 07/11] review fixes --- .../iosSample/iosSample/State/UnsafeObservableState.swift | 4 ++++ .../iosSample/iosSample/View/TextFieldWithControl.swift | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/sample/iosSample/iosSample/State/UnsafeObservableState.swift b/sample/iosSample/iosSample/State/UnsafeObservableState.swift index 1c026f3..7e9797c 100644 --- a/sample/iosSample/iosSample/State/UnsafeObservableState.swift +++ b/sample/iosSample/iosSample/State/UnsafeObservableState.swift @@ -13,6 +13,10 @@ public final class UnsafeObservableState: ObservableObject { self.value = value } } + + func reemitValue() { + value = value + } deinit { self.cancelable?.cancel() diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index dbda534..9525b5b 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -7,9 +7,9 @@ struct TextFieldWithControl: View { @ObservedObject private var hasFocus: UnsafeObservableState @ObservedObject private var isEnabled: UnsafeObservableState - @State private var keyboardOptions: KeyboardOptions @FocusState private var isFocused: Bool + private let keyboardOptions: KeyboardOptions private let inputControl: InputControl private let hint: String private let isSecure: Bool @@ -23,8 +23,6 @@ struct TextFieldWithControl: View { self.error = UnsafeObservableState(inputControl.error) self.hasFocus = UnsafeObservableState(inputControl.hasFocus) self.isEnabled = UnsafeObservableState(inputControl.enabled) - - isFocused = hasFocus.value?.boolValue ?? false } var body: some View { @@ -34,7 +32,7 @@ struct TextFieldWithControl: View { String(text.value ?? "") } set: { value in inputControl.onTextChanged(text: value) - text.value = text.value + text.reemitValue() }, isSecure: isSecure, hint: hint @@ -49,7 +47,7 @@ struct TextFieldWithControl: View { inputControl.onFocusChanged(hasFocus: newValue) } .onChange(of: hasFocus.value?.boolValue ?? false) { newValue in - isFocused = hasFocus.value?.boolValue ?? false + isFocused = newValue } if let error = error.value { From 664214e3b31a5c781cb01b2887025877f4b89ebc Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Fri, 10 Feb 2023 15:57:26 +0300 Subject: [PATCH 08/11] update UI --- .../iosSample/iosSample/View/FormView.swift | 10 ++++++++ .../iosSample/View/SubmitButtonView.swift | 4 +++- .../iosSample/View/TextFieldWithControl.swift | 23 ++++++++++++++----- .../iosSample/iosSample/View/ToggleView.swift | 7 ++++-- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index e9b4f73..f18fb12 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -28,6 +28,14 @@ struct FormView: View { ScrollView(showsIndicators: false) { ScrollViewReader { proxy in VStack{ + HStack { + Text("Default Form") + .font(.largeTitle) + .fontWeight(.bold) + .foregroundColor(.gray) + Spacer() + } + .padding(.vertical, 20) TextFieldWithControl( inputControl: formComponent.nameInput, hint: MR.strings().name_hint.desc().localized(), @@ -103,10 +111,12 @@ struct FormView: View { if(isValid.value?.boolValue ?? false) { Text(MR.strings().success_message.desc().localized()) .padding(8) + .foregroundColor(submitButtonState.value?.toUI()) } Color.clear.padding(.bottom, 70) } + .padding() } } } diff --git a/sample/iosSample/iosSample/View/SubmitButtonView.swift b/sample/iosSample/iosSample/View/SubmitButtonView.swift index 3f2b5f6..09a939c 100644 --- a/sample/iosSample/iosSample/View/SubmitButtonView.swift +++ b/sample/iosSample/iosSample/View/SubmitButtonView.swift @@ -11,10 +11,12 @@ struct SubmitButton: View { action: action, label: { Text(label) - .padding(8) + .padding(.vertical, 8) + .padding(.horizontal, 20) } ) .buttonStyle(BorderedButtonStyle()) .foregroundColor(buttonState.toUI()) + .cornerRadius(20) } } diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index 9525b5b..67b25a5 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -35,6 +35,7 @@ struct TextFieldWithControl: View { text.reemitValue() }, isSecure: isSecure, + isError: error.value == nil, hint: hint ) .disabled(!(isEnabled.value?.boolValue ?? false)) @@ -51,20 +52,33 @@ struct TextFieldWithControl: View { } if let error = error.value { - Text(error.localized()) - .foregroundColor(.red) + HStack { + Text(error.localized()) + .foregroundColor(.red) + Spacer() + } } } - .padding(20) + .padding(.vertical, 10) } private struct TextFieldView: View { @Binding var text: String let isSecure: Bool + let isError: Bool let hint: String var body: some View { + createTextField() + .padding(10) + .overlay { + RoundedRectangle(cornerRadius: 8) + .stroke(isError ? Color.gray : Color.red, lineWidth: 2) + } + } + + @ViewBuilder private func createTextField() -> some View { if isSecure { SecureField( text: $text, @@ -73,11 +87,8 @@ struct TextFieldWithControl: View { Text("") } ) - .textContentType(.password) - .textFieldStyle(.roundedBorder) } else { TextField(hint, text: $text) - .textFieldStyle(.roundedBorder) } } } diff --git a/sample/iosSample/iosSample/View/ToggleView.swift b/sample/iosSample/iosSample/View/ToggleView.swift index 565a199..c5a8549 100644 --- a/sample/iosSample/iosSample/View/ToggleView.swift +++ b/sample/iosSample/iosSample/View/ToggleView.swift @@ -28,8 +28,11 @@ struct ToggleView: View { ) if let error = error.value { - Text(error.localized()) - .foregroundColor(.red) + HStack { + Text(error.localized()) + .foregroundColor(.red) + Spacer() + } } } .padding(4) From aa00591f309574594b38ef9c10593632b9be0af8 Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:17:10 +0300 Subject: [PATCH 09/11] udate ui logic --- .../iosSample/iosSample/View/FormView.swift | 150 ++++++++++-------- 1 file changed, 80 insertions(+), 70 deletions(-) diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index f18fb12..8384bbc 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -27,81 +27,14 @@ struct FormView: View { var body: some View { ScrollView(showsIndicators: false) { ScrollViewReader { proxy in - VStack{ - HStack { - Text("Default Form") - .font(.largeTitle) - .fontWeight(.bold) - .foregroundColor(.gray) - Spacer() - } - .padding(.vertical, 20) - TextFieldWithControl( - inputControl: formComponent.nameInput, - hint: MR.strings().name_hint.desc().localized(), - isSecure: false - ) - .scrolledFocus( - focus: _focus, - id: Field.name.rawValue, - nextId: Field.email.rawValue, - proxy: proxy - ) - .id(Field.name.rawValue) - TextFieldWithControl( - inputControl: formComponent.emailInput, - hint: MR.strings().email_hint.desc().localized(), - isSecure: false - ) - .scrolledFocus( - focus: _focus, - id: Field.email.rawValue, - nextId: Field.phone.rawValue, - proxy: proxy - ) - .id(Field.email.rawValue) - TextFieldWithControl( - inputControl: formComponent.phoneInput, - hint: MR.strings().phone_hint.desc().localized(), - isSecure: false - ) - .scrolledFocus( - focus: _focus, - id: Field.phone.rawValue, - nextId: Field.password.rawValue, - proxy: proxy - ) - .id(Field.phone.rawValue) - TextFieldWithControl( - inputControl: formComponent.passwordInput, - hint: MR.strings().password_hint.desc().localized(), - isSecure: true - ) - .scrolledFocus( - focus: _focus, - id: Field.password.rawValue, - nextId: Field.passwordConfirmation.rawValue, - proxy: proxy - ) - .id(Field.password.rawValue) - TextFieldWithControl( - inputControl: formComponent.confirmPasswordInput, - hint: MR.strings().confirm_password_hint.desc().localized(), - isSecure: true - ) - .scrolledFocus( - focus: _focus, - id: Field.passwordConfirmation.rawValue, - nextId: 5, - proxy: proxy - ) - .id(Field.passwordConfirmation.rawValue) + VStack { + TitleView() + getTextFiledStackView(proxy: proxy) ToggleView( checkControl: formComponent.termsCheckBox, label: MR.strings().terms_hint.desc().localized() ) .id(5) - SubmitButton( label: MR.strings().submit_button.desc().localized(), buttonState: submitButtonState.value!, @@ -120,8 +53,85 @@ struct FormView: View { } } } + + private func getTextFiledStackView(proxy: ScrollViewProxy) -> some View { + VStack { + TextFieldWithControl( + inputControl: formComponent.nameInput, + hint: MR.strings().name_hint.desc().localized(), + isSecure: false + ) + .scrolledFocus( + focus: _focus, + id: Field.name.rawValue, + nextId: Field.email.rawValue, + proxy: proxy + ) + .id(Field.name.rawValue) + TextFieldWithControl( + inputControl: formComponent.emailInput, + hint: MR.strings().email_hint.desc().localized(), + isSecure: false + ) + .scrolledFocus( + focus: _focus, + id: Field.email.rawValue, + nextId: Field.phone.rawValue, + proxy: proxy + ) + .id(Field.email.rawValue) + TextFieldWithControl( + inputControl: formComponent.phoneInput, + hint: MR.strings().phone_hint.desc().localized(), + isSecure: false + ) + .scrolledFocus( + focus: _focus, + id: Field.phone.rawValue, + nextId: Field.password.rawValue, + proxy: proxy + ) + .id(Field.phone.rawValue) + TextFieldWithControl( + inputControl: formComponent.passwordInput, + hint: MR.strings().password_hint.desc().localized(), + isSecure: true + ) + .scrolledFocus( + focus: _focus, + id: Field.password.rawValue, + nextId: Field.passwordConfirmation.rawValue, + proxy: proxy + ) + .id(Field.password.rawValue) + TextFieldWithControl( + inputControl: formComponent.confirmPasswordInput, + hint: MR.strings().confirm_password_hint.desc().localized(), + isSecure: true + ) + .scrolledFocus( + focus: _focus, + id: Field.passwordConfirmation.rawValue, + nextId: 5, + proxy: proxy + ) + .id(Field.passwordConfirmation.rawValue) + } + } } +private struct TitleView: View { + var body: some View { + HStack { + Text("Default Form") + .font(.largeTitle) + .fontWeight(.bold) + .foregroundColor(.gray) + Spacer() + } + .padding(.vertical, 20) + } +} struct FormView_Previews: PreviewProvider { static var previews: some View { From 97a80b80e40f9a23b6343347242a713a543c499d Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:29:23 +0300 Subject: [PATCH 10/11] code-style fix --- .../iosSample/iosSample/View/FormView.swift | 36 ++++++++----------- .../iosSample/View/TextFieldWithControl.swift | 2 +- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index 8384bbc..146002c 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -3,7 +3,6 @@ import sharedSample struct FormView: View { private enum Field: Int { - case name = 0 case email = 1 case phone = 2 @@ -28,7 +27,7 @@ struct FormView: View { ScrollView(showsIndicators: false) { ScrollViewReader { proxy in VStack { - TitleView() + getTitle() getTextFiledStackView(proxy: proxy) ToggleView( checkControl: formComponent.termsCheckBox, @@ -54,12 +53,22 @@ struct FormView: View { } } + private func getTitle() -> some View { + HStack { + Text("Default Form") + .font(.largeTitle) + .fontWeight(.bold) + .foregroundColor(.gray) + Spacer() + } + .padding(.vertical, 20) + } + private func getTextFiledStackView(proxy: ScrollViewProxy) -> some View { VStack { TextFieldWithControl( inputControl: formComponent.nameInput, - hint: MR.strings().name_hint.desc().localized(), - isSecure: false + hint: MR.strings().name_hint.desc().localized() ) .scrolledFocus( focus: _focus, @@ -70,8 +79,7 @@ struct FormView: View { .id(Field.name.rawValue) TextFieldWithControl( inputControl: formComponent.emailInput, - hint: MR.strings().email_hint.desc().localized(), - isSecure: false + hint: MR.strings().email_hint.desc().localized() ) .scrolledFocus( focus: _focus, @@ -82,8 +90,7 @@ struct FormView: View { .id(Field.email.rawValue) TextFieldWithControl( inputControl: formComponent.phoneInput, - hint: MR.strings().phone_hint.desc().localized(), - isSecure: false + hint: MR.strings().phone_hint.desc().localized() ) .scrolledFocus( focus: _focus, @@ -120,19 +127,6 @@ struct FormView: View { } } -private struct TitleView: View { - var body: some View { - HStack { - Text("Default Form") - .font(.largeTitle) - .fontWeight(.bold) - .foregroundColor(.gray) - Spacer() - } - .padding(.vertical, 20) - } -} - struct FormView_Previews: PreviewProvider { static var previews: some View { FormView(formComponent: FakeFormComponent()) diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index 67b25a5..3b2e819 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -14,7 +14,7 @@ struct TextFieldWithControl: View { private let hint: String private let isSecure: Bool - init(inputControl: InputControl, hint: String, isSecure: Bool) { + init(inputControl: InputControl, hint: String, isSecure: Bool = false) { self.hint = hint self.inputControl = inputControl self.isSecure = isSecure From 4c1453d7f6102b46c5c109f42e0145eec4f93c92 Mon Sep 17 00:00:00 2001 From: Nick <80983073+Kotliamba@users.noreply.github.com> Date: Fri, 10 Feb 2023 16:31:40 +0300 Subject: [PATCH 11/11] code-style fix --- sample/iosSample/iosSample/View/FormView.swift | 4 ++-- sample/iosSample/iosSample/View/TextFieldWithControl.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sample/iosSample/iosSample/View/FormView.swift b/sample/iosSample/iosSample/View/FormView.swift index 146002c..c5cf74e 100644 --- a/sample/iosSample/iosSample/View/FormView.swift +++ b/sample/iosSample/iosSample/View/FormView.swift @@ -27,7 +27,7 @@ struct FormView: View { ScrollView(showsIndicators: false) { ScrollViewReader { proxy in VStack { - getTitle() + getTitleView() getTextFiledStackView(proxy: proxy) ToggleView( checkControl: formComponent.termsCheckBox, @@ -53,7 +53,7 @@ struct FormView: View { } } - private func getTitle() -> some View { + private func getTitleView() -> some View { HStack { Text("Default Form") .font(.largeTitle) diff --git a/sample/iosSample/iosSample/View/TextFieldWithControl.swift b/sample/iosSample/iosSample/View/TextFieldWithControl.swift index 3b2e819..6339de9 100644 --- a/sample/iosSample/iosSample/View/TextFieldWithControl.swift +++ b/sample/iosSample/iosSample/View/TextFieldWithControl.swift @@ -78,7 +78,7 @@ struct TextFieldWithControl: View { } } - @ViewBuilder private func createTextField() -> some View { + private func createTextField() -> some View { if isSecure { SecureField( text: $text,