From 929b825e117afd5844983fa6f48dddaa18347f46 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 18 Oct 2019 10:26:47 -0400 Subject: [PATCH] more to uopdate. --- MVMCoreUI.xcodeproj/project.pbxproj | 2 + .../Atoms/TextFields/DigitTextField.swift | 359 ++++++++++++++++-- MVMCoreUI/Atoms/TextFields/MFDigitTextField.m | 2 - 3 files changed, 338 insertions(+), 25 deletions(-) diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 7c1fac20..cea24a21 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; }; 0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; }; 0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; }; + 0A21DB6E2359EEF800C160A2 /* DigitTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8321AC2355FC2600CB7F00 /* DigitTextField.swift */; }; 0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; }; 0A41BA7F23453A6400D4C0BC /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextField.swift */; }; 0A52C1492357B5380051AECD /* MdnTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A8321A72355062F00CB7F00 /* MdnTextField.swift */; }; @@ -1099,6 +1100,7 @@ 0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */, D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */, D29770C821F7C4AE00B2F0D0 /* TopLabelsView.m in Sources */, + 0A21DB6E2359EEF800C160A2 /* DigitTextField.swift in Sources */, D2E1FADF2268B8E700AEFD8C /* ThreeLayerTableViewController.swift in Sources */, D20A9A5E2243D3E300ADE781 /* TwoButtonView.swift in Sources */, D2B1E3E522F37D6A0065F95C /* ImageHeadlineBody.swift in Sources */, diff --git a/MVMCoreUI/Atoms/TextFields/DigitTextField.swift b/MVMCoreUI/Atoms/TextFields/DigitTextField.swift index 7fff621d..79ff733e 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitTextField.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitTextField.swift @@ -40,10 +40,10 @@ import UIKit fatalError("init(coder:) has not been implemented") } - public init(dinumberOfDigits: Int) { + public init(numberOfDigits: Int) { super.init(frame: .zero) - numberOfDigits = dinumberOfDigits + self.numberOfDigits = numberOfDigits buildTextFieldsView(forSize: MVMCoreUISplitViewController.getDetailViewWidth()) } @@ -54,7 +54,7 @@ import UIKit buildTextFieldsView(forSize: MVMCoreUISplitViewController.getDetailViewWidth()) } - public init(withNumberOfDigits numberOfDigits: Int, withBothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?, size: CGFloat) { + public init(withNumberOfDigits numberOfDigits: Int, withBothDelegates delegate: (UITextFie.ldDelegate & MFTextFieldDelegate)?, size: CGFloat) { super.init(bothDelegates: delegate as? (TextFieldDelegate & UITextFieldDelegate)) self.numberOfDigits = numberOfDigits @@ -65,7 +65,7 @@ import UIKit // MARK: - Methods //-------------------------------------------------- - func digitField() -> DigitTextBox? { + func createDigitField() -> DigitTextBox? { let textField = DigitTextBox() textField.delegate = self @@ -82,11 +82,10 @@ import UIKit if numberOfDigits > 0 { - // Create text boxes. var textFields = [AnyHashable](repeating: 0, count: numberOfDigits) for i in 0.. 0 { + if (self?.placeholder!.count)! > 0 { self?.labelToTextFieldPin?.constant = 10 } else { @@ -143,38 +140,42 @@ import UIKit } } - func updateView(_ size: CGFloat) { + open override func updateView(_ size: CGFloat) { super.updateView(size) - MVMCoreDispatchUtility.performBlock(onMainThread: { - self.formLabel.updateView(size) + + DispatchQueue.main.async { [weak self] in + self.formLabel!.updateView(size) // Remove all current UI. if (self.textFields?.count ?? 0) > 0 { - StackableViewController.removeUIViews(self.textFields) + StackableViewController.remove(self.textFields) } // Update text boxes. for textField in self.textFields ?? [] { - guard let textField = textField as? MFDigitTextBox else { - continue - } + guard let textField = textField as? MFDigitTextBox else { continue } textField.updateView(size) } // Layout text boxes. self.setupTextFieldsView(forSize: size) - }) + } } - func setupView() { + open override func setupView() { super.setupView() - formLabel.styleB2(true) + + formLabel?.styleB2(true) self.formText = "" alignCenterHorizontal() } - + + //-------------------------------------------------- // MARK: - Molecule - func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + //-------------------------------------------------- + + open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + let digitsNumber = json?.optionalNumber(forKey: "digits") let digits = digitsNumber != nil ? digitsNumber?.intValue ?? 0 : 4 if digits != numberOfDigits { @@ -184,7 +185,319 @@ import UIKit super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) } - class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + open override class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { + return 44 } + + //-------------------------------------------------- + // MARK: - Getters + //-------------------------------------------------- + + func placeholder() -> String? { + + var string = "" + for textField in textFields ?? [] { + if textField.attributedPlaceholder?.string != nil { + string += textField.attributedPlaceholder?.string ?? "" + } + } + return string.count > 0 ? string : nil + } + + func text() -> String? { + + var string = "" + for textField in textFields ?? [] { + if textField.text != nil { + string += textField.text ?? "" + } + } + return string + } + + //-------------------------------------------------- + // MARK: - Setters + //-------------------------------------------------- + + func setFormText(_ formText: String?) { + + if (formText?.count ?? 0) > 0 { + messageToTextFieldPin?.constant = 10 + } else { + messageToTextFieldPin?.constant = 0 + } + super.setFormText(formText) + } + + func setAsSecureTextEntry(_ secureTextEntry: Bool) { + + DispatchQueue.main.async { [weak self] in + + (self.textFields as NSArray?)?.enumerateObjects({ obj, idx, stop in + obj.isSecureTextEntry = secureTextEntry + + //accessibility - 33704 fix voice over will read what pin user is filling + obj.accessibilityLabel() = String(format: "PIN %lu of %lu", UInt(idx) + 1, UInt(self.textFields?.count ?? 0)) + }) + } + } + + func setPlaceholder(_ placeholder: String?) { + + if placeholder != nil { + (textFields as NSArray?)?.enumerateObjects({ obj, idx, stop in + + if idx < (placeholder?.count ?? 0) { + + let stringForIndex = (placeholder as NSString?)?.substring(with: NSRange(location: idx, length: 1)) + obj.attributedPlaceholder = NSAttributedString(string: stringForIndex ?? "", attributes: [ + NSAttributedString.Key.foregroundColor: UIColor.mfBattleshipGrey() + ]) + } else if stop != nil { + stop = true + } + }) + } + + // If there is already text in the textfield, set the place holder label below. + if text.length > 0 && !errorShowing { + label.text = placeholder + + } else if !errorShowing { + label.text = "" + } + + if label.text.length > 0 { + labelToTextFieldPin?.constant = 10 + } else { + labelToTextFieldPin?.constant = 0 + } + + // adding missing accessibilityLabel value + // if we have some value in accessibilityLabel, + // then only can append regular and picker item + textField.accessibilityLabel() = placeholder ?? "" + (MVMCoreUIUtility.hardcodedString(withKey: "mfdigittextfield_regular")) + } + + func setErrorMessage(_ errorMessage: String?) { + DispatchQueue.main.async { [weak self] in + super.setErrorMessage(errorMessage) + if self.errorShowing { + self.labelToTextFieldPin?.constant = 10 + } + (self.textFields as NSArray?)?.enumerateObjects({ obj, idx, stop in + obj.setAsError() + }) + } + } + + public override func hideError() { + DispatchQueue.main.async { [weak self] in + super.hideError() + (self.textFields as NSArray?)?.enumerateObjects({ obj, idx, stop in + obj.hideError() + }) + } + } + + func setText(_ text: String?) { + (textFields as NSArray?)?.enumerateObjects({ obj, idx, stop in + if idx < (text?.count ?? 0) { + let stringForIndex = (text as NSString?)?.substring(with: NSRange(location: idx, length: 1)) + obj.text = stringForIndex + } else if stop != nil { + stop = true + } + }) + valueChanged() + } + + func setWithMap(_ map: [AnyHashable : Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { + super.setWithMap(map, bothDelegates: delegate) + if (map?.count ?? 0) > 0 { + for textField in textFields ?? [] { + MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegate) + } + } + } + + func setDefaultValidationBlock() { + + weak var weakSelf = self + self.validationBlock = { enteredValue in + if (enteredValue?.count ?? 0) > 0 && (enteredValue?.count ?? 0) == weakSelf?.textFields?.count { + return true + } else { + return false + } + } + } + + func enable(_ enable: Bool) { + super.enable(enable) + + if enable { + formLabel?.styleB2(true) + } else { + formLabel?.textColor = UIColor.mfBattleshipGrey() + } + + for textField in textFields ?? [] { + textField.isUserInteractionEnabled = enable + textField.isEnabled = enable + if enable { + textField.textColor = UIColor.black + } else { + textField.textColor = UIColor.mfBattleshipGrey() + } + } + } + + //-------------------------------------------------- + // MARK: - Helpers + //-------------------------------------------------- + + func selectPreviousTextField(_ currentTextField: UITextField?, clear: Bool) { + + var selectNextField = false + (textFields as NSArray?)?.enumerateObjects(options: .reverse, using: { obj, idx, stop in + if obj == currentTextField { + selectNextField = true + } else if selectNextField { + if !clear { + self.switchedAutomatically = true + } + obj.becomeFirstResponder() + self.switchedAutomatically = false + stop = true + + //accessibility + UIAccessibility.post(notification: .layoutChanged, argument: obj) + } + }) + } + + func selectNextTextField(_ currentTextField: UITextField?, clear: Bool) { + + var selectNextField = false + (textFields as NSArray?)?.enumerateObjects({ obj, idx, stop in + if obj == currentTextField { + selectNextField = true + } else if selectNextField { + if !clear { + self.switchedAutomatically = true + } + obj.becomeFirstResponder() + self.switchedAutomatically = false + stop = true + + //accessibility + UIAccessibility.post(notification: .layoutChanged, argument: obj) + } + }) + } + + //-------------------------------------------------- + // MARK: - Accessinility + //-------------------------------------------------- + + open override var accessibilityElements: [Any]? { + if (self.textFields) { + return [self.textFields arrayByAddingObject:(MFDigitTextBox *)self.label]; + } else { + return @[self.label]; + } + } + + //-------------------------------------------------- + // MARK: - TextFieldDelegate + //-------------------------------------------------- + + @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.resignFirstResponder() + + if uiTextFieldDelegate!.responds(to: #selector(UITextFieldDelegate.textFieldShouldReturn(_:))) { + return uiTextFieldDelegate!.textFieldShouldReturn!(textField) + } + return true + } + + public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + + if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) { + return false + } + + if (textField.text?.count ?? 0) >= range.length + range.location { + + let oldLength = textField.text?.count ?? 0 + let replacementLength = string.count + if replacementLength > 1 { + + // Too long (Check with AKQA if they want to allow pasting the digits. + return false + } else if replacementLength == 1 && (oldLength == 1 || oldLength == 0) { + + // One character, switch old value with new, select next textfield + textField.text = string + selectNextTextField(textField, clear: false) + valueChanged() + return false + } else if replacementLength == 0 && oldLength == 1 { + // non empty cell, clear and stay. + textField.text = "" + valueChanged() + return false + } + return true + } else { + return false + } + } + + func textFieldDidDelete(_ textField: UITextField?) { + // empty cell, go back to previous cell and clear. + selectPreviousTextField(textField, clear: true) + } + + @objc public func textFieldDidBeginEditing(_ textField: UITextField) { + if !switchedAutomatically { + textField.text = "" + valueChanged() + } + if uiTextFieldDelegate?.responds(to: #selector(UITextFieldDelegate.textFieldDidBeginEditing(_:))) { + uiTextFieldDelegate?.textFieldDidBeginEditing(textField) + } + } + + @objc public func textFieldDidEndEditing(_ textField: UITextField) { + if uiTextFieldDelegate!.responds(to: #selector(UITextFieldDelegate.textFieldDidEndEditing(_:))) { + uiTextFieldDelegate!.textFieldDidEndEditing(textField) + } + } + + @objc public func textFieldShouldClear(_ textField: UITextField) -> Bool { + selectPreviousTextField(textField, clear: false) + + if uiTextFieldDelegate?.responds(to: #selector(UITextFieldDelegate.textFieldShouldClear(_:))) { + return uiTextFieldDelegate?.textFieldShouldClear(textField) + } + return true + } + + // MARK: - Passed Along TextField delegate + @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { + if uiTextFieldDelegate?.responds(to: #selector(UITextFieldDelegate.textFieldShouldBeginEditing(_:))) { + return uiTextFieldDelegate?.textFieldShouldBeginEditing(textField) + } + return true + } + + @objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { + if uiTextFieldDelegate!.responds(to: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:))) { + return uiTextFieldDelegate?.textFieldShouldEndEditing(textField) + } + return true + } } diff --git a/MVMCoreUI/Atoms/TextFields/MFDigitTextField.m b/MVMCoreUI/Atoms/TextFields/MFDigitTextField.m index 3e00d6f5..5f76e88b 100644 --- a/MVMCoreUI/Atoms/TextFields/MFDigitTextField.m +++ b/MVMCoreUI/Atoms/TextFields/MFDigitTextField.m @@ -161,8 +161,6 @@ return 44; } -// CONTINUE - #pragma mark - Getters - (NSString *)placeholder {