diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..53b16c0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "code-quality"] + path = code-quality + url = https://github.com/TouchInstinct/code-quality-ios diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8dada3e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LeadKitAdditions.podspec b/LeadKitAdditions.podspec index 07379bb..dfc9623 100644 --- a/LeadKitAdditions.podspec +++ b/LeadKitAdditions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKitAdditions" - s.version = "0.0.20" + s.version = "0.0.21" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKitAdditions" s.license = "Apache License, Version 2.0" @@ -34,6 +34,8 @@ Pod::Spec.new do |s| ss.dependency "LeadKit/Core-iOS-Extension", '~> 0.5' ss.dependency "KeychainAccess", '3.0.2' ss.dependency "IDZSwiftCommonCrypto", '0.9.1' + ss.dependency "InputMask", '2.2.5' + ss.dependency "SwiftValidator", '4.0.0' end s.default_subspec = 'Core' diff --git a/LeadKitAdditions/.swiftlint.yml b/LeadKitAdditions/.swiftlint.yml deleted file mode 100644 index 307a6ea..0000000 --- a/LeadKitAdditions/.swiftlint.yml +++ /dev/null @@ -1,21 +0,0 @@ -disabled_rules: - - variable_name -excluded: - - Carthage - - Pods -line_length: 128 -type_body_length: - - 500 # warning - - 700 # error -file_length: - warning: 500 - error: 1200 -warning_threshold: 1 - -custom_rules: - uiwebview_disabled: - included: ".*.swift" - name: "UIWebView Usage Disabled" - regex: "(UIWebView)" - message: "Do not use UIWebView. Use WKWebView Instead. https://developer.apple.com/reference/uikit/uiwebview" - severity: error \ No newline at end of file diff --git a/LeadKitAdditions/.swiftlint.yml b/LeadKitAdditions/.swiftlint.yml new file mode 120000 index 0000000..7fc22af --- /dev/null +++ b/LeadKitAdditions/.swiftlint.yml @@ -0,0 +1 @@ +../code-quality/.swiftlint.yml \ No newline at end of file diff --git a/LeadKitAdditions/.tailor.yml b/LeadKitAdditions/.tailor.yml deleted file mode 100644 index 1bee518..0000000 --- a/LeadKitAdditions/.tailor.yml +++ /dev/null @@ -1,4 +0,0 @@ -exclude: - - 'Pods' - - 'Carthage' - - 'RxAlamofire' diff --git a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj index fa859bb..d511837 100644 --- a/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj +++ b/LeadKitAdditions/LeadKitAdditions.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0A08E37F1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */; }; 248389A288C0A6D7914F0546 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */; }; B326804BA6CC8B8BB136A46A /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFD5627139CAB27705F75C07 /* Pods_LeadKitAdditions_LeadKitAdditions_iOS_Extensions.framework */; }; CAE698E81E968820000394B0 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -78,11 +79,13 @@ ED0C34461F2906EC00FAE9FD /* CellTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34011F2906EC00FAE9FD /* CellTextField.swift */; }; ED0C34471F2906EC00FAE9FD /* CellTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34021F2906EC00FAE9FD /* CellTextFieldViewModel.swift */; }; ED0C34481F2906EC00FAE9FD /* CellTextFieldViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0C34021F2906EC00FAE9FD /* CellTextFieldViewModel.swift */; }; + ED69E7E81F2AC7CC00C74895 /* CellTextFieldToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */; }; EFBD55921EBB9A980062AA63 /* LeadKitAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = CAE698E61E968820000394B0 /* LeadKitAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 01605ECA03749D49C27FA3DD /* Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions/Pods-LeadKitAdditions-LeadKitAdditions iOS Extensions.release.xcconfig"; sourceTree = ""; }; + 0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CellTextFieldToolBar.swift; sourceTree = ""; }; 0ED4A1B793EAA73C9E95969F /* Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions_LeadKitAdditions_iOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49738551AC648B0AFA74E57F /* Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-LeadKitAdditions-LeadKitAdditions iOS/Pods-LeadKitAdditions-LeadKitAdditions iOS.debug.xcconfig"; sourceTree = ""; }; 7B7F57C5E5275C4D8DC71992 /* Pods_LeadKitAdditions.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LeadKitAdditions.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -323,6 +326,7 @@ ED0C33FF1F2906EC00FAE9FD /* Views */ = { isa = PBXGroup; children = ( + 0A08E37E1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift */, ED0C34001F2906EC00FAE9FD /* CellTextField */, ); path = Views; @@ -381,7 +385,6 @@ CAE698E11E968820000394B0 /* Resources */, 94F6E1BA5AD68C6E2F10062B /* [CP] Copy Pods Resources */, CAE6990A1E969A7A000394B0 /* Swiftlint */, - CAE6990B1E969A8D000394B0 /* Tailor */, ); buildRules = ( ); @@ -402,6 +405,7 @@ EFBD556D1EBB87100062AA63 /* Headers */, EFBD556E1EBB87100062AA63 /* Resources */, 808FF5474C0E1574D405EFAF /* [CP] Copy Pods Resources */, + ED69E7E91F2AD0E000C74895 /* Swiftlint */, ); buildRules = ( ); @@ -527,20 +531,6 @@ shellPath = /bin/sh; shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; }; - CAE6990B1E969A8D000394B0 /* Tailor */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = Tailor; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if hash tailor 2>/dev/null; then\n tailor\nelse\n echo \"warning: Please install Tailor from https://tailor.sh\"\nfi"; - }; E8E82E34792B38EF225575D7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -556,6 +546,20 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; + ED69E7E91F2AD0E000C74895 /* Swiftlint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = Swiftlint; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -585,6 +589,7 @@ ED0C34451F2906EC00FAE9FD /* CellTextField.swift in Sources */, ED0C34271F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift in Sources */, ED0C34291F2906EC00FAE9FD /* CellFieldValidationProtocol.swift in Sources */, + 0A08E37F1F2A13BF00F9AB62 /* CellTextFieldToolBar.swift in Sources */, ED0C34031F2906EC00FAE9FD /* ApiResponse.swift in Sources */, ED0C34071F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */, ED0C34251F2906EC00FAE9FD /* CellFieldMaskProtocol.swift in Sources */, @@ -626,6 +631,7 @@ ED0C340E1F2906EC00FAE9FD /* PassCodeHolder.swift in Sources */, ED0C34461F2906EC00FAE9FD /* CellTextField.swift in Sources */, ED0C34281F2906EC00FAE9FD /* CellFieldsToolBarProtocol.swift in Sources */, + ED69E7E81F2AC7CC00C74895 /* CellTextFieldToolBar.swift in Sources */, ED0C342A1F2906EC00FAE9FD /* CellFieldValidationProtocol.swift in Sources */, ED0C34041F2906EC00FAE9FD /* ApiResponse.swift in Sources */, ED0C34081F2906EC00FAE9FD /* LoadingBarButton.swift in Sources */, diff --git a/LeadKitAdditions/Sources/Controllers/PassCode/View/BasePassCodeViewController.swift b/LeadKitAdditions/Sources/Controllers/PassCode/View/BasePassCodeViewController.swift index 11591ce..ccb7918 100644 --- a/LeadKitAdditions/Sources/Controllers/PassCode/View/BasePassCodeViewController.swift +++ b/LeadKitAdditions/Sources/Controllers/PassCode/View/BasePassCodeViewController.swift @@ -212,11 +212,11 @@ extension BasePassCodeViewController: ConfigurableController { open func bindViews() { fakeTextField.rx.text.asDriver() - .do(onNext: { [weak self] text in + .drive(onNext: { [weak self] text in self?.setStates(for: text ?? "") self?.hideError() + self?.viewModel.setPassCodeText(text) }) - .drive(viewModel.passCodeText) .addDisposableTo(disposeBag) viewModel.validationResult diff --git a/LeadKitAdditions/Sources/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift b/LeadKitAdditions/Sources/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift index 3fbd333..f17ca41 100644 --- a/LeadKitAdditions/Sources/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift +++ b/LeadKitAdditions/Sources/Controllers/PassCode/ViewModel/BasePassCodeViewModel.swift @@ -52,7 +52,7 @@ open class BasePassCodeViewModel: BaseViewModel { return passCodeControllerStateHolder.asDriver() } - public let passCodeText = Variable(nil) + private let passCodeText = Variable(nil) fileprivate var attemptsNumber = 0 @@ -108,6 +108,16 @@ open class BasePassCodeViewModel: BaseViewModel { .addDisposableTo(disposeBag) } + // MARK: - Public + + public var passCodeTextValue: String? { + return passCodeText.value + } + + public func setPassCodeText(_ value: String?) { + passCodeText.value = value + } + public func reset() { passCodeText.value = nil validationResultHolder.value = nil diff --git a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift index 6406c78..692ff28 100644 --- a/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift +++ b/LeadKitAdditions/Sources/Services/CellFieldsJumpingService.swift @@ -77,36 +77,33 @@ class CellFieldsJumpingService { } private func toolBar(for field: CellFieldJumpingProtocol, with index: Int) -> UIToolbar { - // hotfix for project builing - // todo: @GrigoryUlanov - return UIToolbar() -// let toolBar = CellTextFieldToolBar() -// toolBar.canGoForward = cellFields.nextActive(from: index) != nil -// toolBar.canGoBackward = cellFields.previousActive(from: index) != nil -// -// toolBar.needArrows = config.toolBarNeedArrows -// -// toolBar.shouldGoForward.asObservable() -// .subscribe(onNext: { [weak self] in -// self?.shouldGoForwardAction(from: index) -// }) -// .addDisposableTo(disposeBag) -// -// toolBar.shouldGoBackward.asObservable() -// .subscribe(onNext: { [weak self] in -// if let previousActive = self?.cellFields.previousActive(from: index) { -// previousActive.shouldBecomeFirstResponder.onNext() -// } -// }) -// .addDisposableTo(disposeBag) -// -// toolBar.shouldEndEditing.asObservable() -// .subscribe(onNext: { -// field.shouldResignFirstResponder.onNext() -// }) -// .addDisposableTo(disposeBag) -// -// return toolBar + let toolBar = CellTextFieldToolBar() + toolBar.canGoForward = cellFields.nextActive(from: index) != nil + toolBar.canGoBackward = cellFields.previousActive(from: index) != nil + + toolBar.needArrows = config.toolBarNeedArrows + + toolBar.shouldGoForward.asObservable() + .subscribe(onNext: { [weak self] in + self?.shouldGoForwardAction(from: index) + }) + .addDisposableTo(disposeBag) + + toolBar.shouldGoBackward.asObservable() + .subscribe(onNext: { [weak self] in + if let previousActive = self?.cellFields.previousActive(from: index) { + previousActive.shouldBecomeFirstResponder.onNext() + } + }) + .addDisposableTo(disposeBag) + + toolBar.shouldEndEditing.asObservable() + .subscribe(onNext: { + field.shouldResignFirstResponder.onNext() + }) + .addDisposableTo(disposeBag) + + return toolBar } private func shouldGoForwardAction(from index: Int) { diff --git a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift index e28c424..a275c51 100644 --- a/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift +++ b/LeadKitAdditions/Sources/Services/MaskFieldTextProxy.swift @@ -6,7 +6,8 @@ class MaskFieldTextProxy: NSObject { private var disposeBag = DisposeBag() - let text = Variable("") + fileprivate let text = Variable("") + fileprivate let isCompleteHolder = Variable(false) var isComplete: Bool { return isCompleteHolder.value diff --git a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift index 902ac93..e848cd5 100644 --- a/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift +++ b/LeadKitAdditions/Sources/Services/ValidationService/ValidationItem.swift @@ -43,7 +43,7 @@ class ValidationItem { return validationStateHolder.asObservable() } - let text = Variable(nil) + private let text = Variable(nil) private(set) var rules: [Rule] = [] diff --git a/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift b/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift index 4ccf4ac..0377b83 100644 --- a/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift +++ b/LeadKitAdditions/Sources/Views/CellTextField/CellTextField.swift @@ -24,14 +24,16 @@ class CellTextField: UITextField { inputAccessoryView = viewModel.toolBar returnKeyType = viewModel.returnButtonType - text = viewModel.text.value + text = viewModel.textValue placeholder = viewModel.placeholder viewModel.textFieldSettingsBlock?(self) viewModel.bind(for: self, to: disposeBag) rx.text.asDriver() - .drive(viewModel.text) + .drive(onNext: { + viewModel.setTextValue($0) + }) .addDisposableTo(disposeBag) rx.controlEvent(.editingDidEndOnExit).asObservable() diff --git a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift index c154ede..b3508f8 100644 --- a/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift +++ b/LeadKitAdditions/Sources/Views/CellTextField/CellTextFieldViewModel.swift @@ -3,7 +3,8 @@ import RxSwift class CellTextFieldViewModel: CellFieldJumpingProtocol { - let text: Variable + private let text: Variable + let placeholder: String let textFieldSettingsBlock: ItemSettingsBlock? @@ -27,4 +28,14 @@ class CellTextFieldViewModel: CellFieldJumpingProtocol { self.textFieldSettingsBlock = textFieldSettingsBlock } + // MARK: - Internal + + var textValue: String? { + return text.value + } + + func setTextValue(_ value: String?) { + text.value = value + } + } diff --git a/LeadKitAdditions/Sources/Views/CellTextFieldToolBar.swift b/LeadKitAdditions/Sources/Views/CellTextFieldToolBar.swift new file mode 100644 index 0000000..da15cb9 --- /dev/null +++ b/LeadKitAdditions/Sources/Views/CellTextFieldToolBar.swift @@ -0,0 +1,107 @@ +import UIKit +import RxSwift +import RxCocoa +import LeadKit + +class CellTextFieldToolBar: UIToolbar, CellFieldsToolBarProtocol { + + private let buttonSpace: CGFloat = 20 + + // MARK: - CellFieldsToolBarProtocol + + var needArrows: Bool = true + + var canGoForward: Bool = false { + didSet { + forwardButton.isEnabled = canGoForward + } + } + var canGoBackward: Bool = false { + didSet { + backButton.isEnabled = canGoBackward + } + } + + var shouldGoForward = PublishSubject() + var shouldGoBackward = PublishSubject() + var shouldEndEditing = PublishSubject() + + // MARK: - UIBarButtonItems + + var backButtonImage: UIImage? { + didSet { + backButton.image = backButtonImage + } + } + var forwardButtonImage: UIImage? { + didSet { + backButton.image = backButtonImage + } + } + + private(set) lazy var backButton: UIBarButtonItem = { + let backButton = UIBarButtonItem(image: self.backButtonImage, + style: .plain, + target: self, + action: #selector(backAction)) + return backButton + }() + + private(set) lazy var forwardButton: UIBarButtonItem = { + let forwardButton = UIBarButtonItem(image: self.forwardButtonImage, + style: .plain, + target: self, + action: #selector(forwardAction)) + return forwardButton + }() + + private(set) lazy var closeButton: UIBarButtonItem = { + let doneButton = UIBarButtonItem(barButtonSystemItem: .done, + target: self, + action: #selector(doneAction)) + return doneButton + }() + + // MARK: - Initialization + + override init(frame: CGRect) { + super.init(frame: frame) + initialization() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + initialization() + } + + private func initialization() { + barStyle = .default + isTranslucent = true + tintColor = UIColor(hex6: 0x0A84DF) + sizeToFit() + + let leftSpacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) + let firstButtonsSpacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + firstButtonsSpacer.width = buttonSpace + let secondButtonsSpacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) + secondButtonsSpacer.width = buttonSpace + + setItems([leftSpacer, backButton, firstButtonsSpacer, forwardButton, secondButtonsSpacer, closeButton], animated: true) + items?.forEach { $0.tintColor = tintColor } + } + + // MARK: - Actions + + @objc private func backAction() { + shouldGoBackward.onNext() + } + + @objc private func forwardAction() { + shouldGoForward.onNext() + } + + @objc private func doneAction() { + shouldEndEditing.onNext() + } + +} diff --git a/code-quality b/code-quality new file mode 160000 index 0000000..817e845 --- /dev/null +++ b/code-quality @@ -0,0 +1 @@ +Subproject commit 817e845592f02fb0d22bf52ff9bb8db3210ddd89