From a5ffae9edd58b64ef92c11e9bee1de1d0bc059fd Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Wed, 23 Oct 2019 15:15:23 -0400 Subject: [PATCH] Latest state. --- MVMCoreUI.xcodeproj/project.pbxproj | 2 - .../Atoms/TextFields/DigitEntryField.swift | 4 +- .../Atoms/TextFields/DropdownEntryField.swift | 6 +- .../Atoms/TextFields/FormEntryField.swift | 23 +- .../Atoms/TextFields/MdnEntryField.swift | 46 +- .../Atoms/TextFields/TextEntryField.swift | 337 +++----- MVMCoreUI/Atoms/TextFields/TextField.swift | 799 ------------------ 7 files changed, 130 insertions(+), 1087 deletions(-) delete mode 100644 MVMCoreUI/Atoms/TextFields/TextField.swift diff --git a/MVMCoreUI.xcodeproj/project.pbxproj b/MVMCoreUI.xcodeproj/project.pbxproj index 5c31b47b..8cf0af96 100644 --- a/MVMCoreUI.xcodeproj/project.pbxproj +++ b/MVMCoreUI.xcodeproj/project.pbxproj @@ -213,7 +213,6 @@ 01DF566F21FA5AB300CC099B /* TextFieldListFormViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextFieldListFormViewController.swift; sourceTree = ""; }; 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = ""; }; 0A21DB7E235DECC500C160A2 /* FormEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormEntryField.swift; sourceTree = ""; }; - 0A21DB80235DF87300C160A2 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnEntryField.swift; sourceTree = ""; }; 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryField.swift; sourceTree = ""; }; 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = ""; }; @@ -769,7 +768,6 @@ 0A8321AC2355FC2600CB7F00 /* DigitTextField.swift */, 0A8321AE2355FE9500CB7F00 /* DigitTextBox.swift */, 0A21DB7E235DECC500C160A2 /* FormEntryField.swift */, - 0A21DB80235DF87300C160A2 /* TextField.swift */, 0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */, 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */, 0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */, diff --git a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift b/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift index 346c1c9c..20b9965d 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitEntryField.swift @@ -9,7 +9,7 @@ import UIKit -class DigitEntryField: TextEntryField, UITextFieldDelegate, DigitTextBoxDelegate { +class DigitEntryField: TextEntryField, DigitTextBoxDelegate { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- @@ -25,7 +25,7 @@ class DigitEntryField: TextEntryField, UITextFieldDelegate, DigitTextBoxDelegate public var digitFields: [DigitTextBox]? /// Setgs placeholder text in the textField. - public override var placeholder: String? { + public override var feedback: String? { get { var string = "" diff --git a/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift b/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift index 005c5e8e..2448ccef 100644 --- a/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift @@ -358,7 +358,7 @@ import UIKit } // MARK: - Date Picker -extension TextEntryField { +extension DropdownEntryField { private func createDatePicker() { @@ -426,7 +426,7 @@ extension TextEntryField { } // MARK: - Molecular -extension TextEntryField { +extension DropdownEntryField { override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) @@ -453,7 +453,7 @@ extension TextEntryField { } // MARK: - Accessibility -extension TextEntryField { +extension DropdownEntryField { open override func pushAccessibilityNotification() { diff --git a/MVMCoreUI/Atoms/TextFields/FormEntryField.swift b/MVMCoreUI/Atoms/TextFields/FormEntryField.swift index 1f92bcd7..cd1042b3 100644 --- a/MVMCoreUI/Atoms/TextFields/FormEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/FormEntryField.swift @@ -25,14 +25,6 @@ import UIKit public var separatorView: UIView? public var dashLine: DashLine? - //-------------------------------------------------- - // MARK: - Accessories - //-------------------------------------------------- - - public weak var datePicker: UIDatePicker? - public var pickerView: UIPickerView? - private(set) weak var toolbar: UIToolbar? - //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -42,7 +34,12 @@ import UIKit private var borderPath: UIBezierPath? - public var errorMessage: String? + public var errorMessage: String? { + didSet { + feedback = errorMessage + } + } + public var showErrorMessage = false public var isEnabled = true { @@ -83,21 +80,19 @@ import UIKit public var feedback: String? { get { return feedbackLabel?.text } set { - guard isEnabled, - let newFeedback = newValue - else { return } + guard isEnabled else { return } DispatchQueue.main.async { [weak self] in guard let self = self else { return } - self.feedbackLabel?.text = newFeedback + self.feedbackLabel?.text = newValue self.separatorHeightConstraint?.constant = self.showErrorMessage ? 4 : 1 self.separatorView?.backgroundColor = self.showErrorMessage ? UIColor.mfPumpkin() : .black self.setNeedsDisplay() self.layoutIfNeeded() } - setAccessibilityString(newFeedback) + setAccessibilityString(newValue) } } diff --git a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift b/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift index ff4316e9..7d2ad7a7 100644 --- a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift @@ -11,20 +11,20 @@ import ContactsUI import UIKit import MVMCore -class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigationControllerDelegate, CNContactPickerDelegate { + +class MdnEntryField: TextEntryField, ABPeoplePickerNavigationControllerDelegate, CNContactPickerDelegate { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- public weak var customDelegate: UITextFieldDelegate? + public var isNationalMdn = false public var shouldValidateMDN = false public var mdn: String? { get { return MVMCoreUIUtility.removeMdnFormat(text) } - set { - text = MVMCoreUIUtility.formatMdn(newValue) - } + set { text = MVMCoreUIUtility.formatMdn(newValue) } } //-------------------------------------------------- @@ -70,10 +70,6 @@ class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigati set { super.uiTextFieldDelegate = newValue customDelegate = uiTextFieldDelegate - - if newValue != nil { - textField?.delegate = self - } } } @@ -81,13 +77,11 @@ class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigati // MARK: - Methods //-------------------------------------------------- - func hasValidMdn() -> Bool { + func hasValidMDN() -> Bool { - guard let MDN = mdn else { return true } - - if MDN.isEmpty { - return true - } + guard let MDN = mdn, + !MDN.isEmpty + else { return true } if isNationalMdn { return MVMCoreUIUtility.validateMDNString(MDN) @@ -99,12 +93,12 @@ class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigati func validateAndColor() -> Bool { if !shouldValidateMDN { - let isValid = hasValidMdn() + let isValid = hasValidMDN() if isValid { - hideError() + clearError() } else { - self.errorMessage = getErrorMessage() ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") + errorMessage = errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: textField) } @@ -114,18 +108,9 @@ class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigati return true } - func getErrorMessage() -> String? { - - return nil - } - @objc func dismissFieldInput(_ sender: Any?) { - if let delegate = uiTextFieldDelegate { - delegate.perform(#selector(dismissFieldInput(_:)), with: textField) - } else { - textField?.resignFirstResponder() - } + textField?.resignFirstResponder() } func getContacts(_ sender: Any?) { @@ -144,10 +129,9 @@ class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigati public func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) { - if contactProperty.value != nil && (contactProperty.value is CNPhoneNumber) { + if let phoneNumber = contactProperty.value as? CNPhoneNumber { - let phoneNumber = contactProperty.value as? CNPhoneNumber - let MDN = phoneNumber?.stringValue + let MDN = phoneNumber.stringValue var unformattedMDN = MVMCoreUIUtility.removeMdnFormat(MDN) // Sometimes user add extra 1 in front of mdn in their address book @@ -169,7 +153,7 @@ class MdnEntryField: TextEntryField, UITextFieldDelegate, ABPeoplePickerNavigati } //-------------------------------------------------- - // MARK: - ImplementedTextField Delegate + // MARK: - Implemented TextField Delegate //-------------------------------------------------- @discardableResult diff --git a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift index 82d27df1..63a5ce1b 100644 --- a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift @@ -19,7 +19,7 @@ import UIKit } -@objcMembers open class TextEntryField: FormEntryField { +@objcMembers open class TextEntryField: FormEntryField, UITextFieldDelegate { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- @@ -27,6 +27,13 @@ import UIKit private(set) var textField: UITextField? private var calendar: Calendar? + //-------------------------------------------------- + // MARK: - Accessories + //-------------------------------------------------- + + public weak var datePicker: UIDatePicker? + public var pickerView: UIPickerView? + //-------------------------------------------------- // MARK: - Delegate Properties //-------------------------------------------------- @@ -68,6 +75,7 @@ import UIKit DispatchQueue.main.async { [weak self] in guard let self = self else { return } + self.textField?.isEnabled = self.isEnabled self.textField?.textColor = self.isEnabled ? self.enabledTextColor : self.enabledTextColor } } @@ -88,6 +96,18 @@ import UIKit } } + public override var errorMessage: String? { + didSet { + textField?.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message") ?? "", textField?.text ?? "", errorMessage ?? "") + } + } + + //-------------------------------------------------- + // MARK: - Constraints + //-------------------------------------------------- + + public var textFieldTrailingConstraint: NSLayoutConstraint? + //-------------------------------------------------- // MARK: - Initializers //-------------------------------------------------- @@ -111,7 +131,7 @@ import UIKit public init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { super.init(frame: .zero) setupView() - setBothTextFieldDelegates(bothDelegates) + setBothTextDelegates(bothDelegates) } //-------------------------------------------------- @@ -137,6 +157,9 @@ import UIKit textField.topAnchor.constraint(equalTo: container.topAnchor, constant: 10), textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16), container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 10)]) + + textFieldTrailingConstraint = container.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 16) + textFieldTrailingConstraint?.isActive = true } open override func updateView(_ size: CGFloat) { @@ -158,66 +181,91 @@ import UIKit // MARK: - Methods //-------------------------------------------------- - public func showDropDown(_ show: Bool) { - - if hasDropDown { - dropDownCaretLabel?.isHidden = !show - dropDownCarrotWidth?.isActive = !show - setNeedsLayout() - layoutIfNeeded() - } - } - - open override func showErrorMessage(_ errorMessage: String?) { - super.showErrorMessage(errorMessage) - - textField?.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message") ?? "", textField?.text ?? "", errorMessage ?? "") - } - - open override func hideError() { - super.hideError() + open func clearError() { + feedback = nil textField?.accessibilityValue = nil } - public func setBothTextFieldDelegates(_ delegate: (UITextFieldDelegate & TextFieldDelegate)?) { + public func setBothTextDelegates(_ delegate: (UITextFieldDelegate & TextFieldDelegate)?) { mfTextFieldDelegate = delegate uiTextFieldDelegate = delegate } - public override func setWithMap(_ map: [AnyHashable: Any]?) { - super.setWithMap(map) + public func defaultValidationBlock() { - guard let map = map, !map.isEmpty else { return } + validationBlock = { enteredValue in + return (enteredValue?.count ?? 0) > 0 + } + } + + //-------------------------------------------------- + // MARK: - Observing for change + //-------------------------------------------------- + + func valueChanged() { - if let formText = map[KeyLabel] as? String { - self.formText = formText + if !showErrorMessage { + feedback = "" } - if let text = map[KeyValue] as? String { - self.text = text - } + let previousValidity = isValid - if let text = map[KeyDisable] as? String, text.isEqual(StringY) || map.boolForKey(KeyDisable) { - formIsDisabled() - } + // If validation not set, input will always be valid + isValid = validationBlock?(text) ?? true - if let dropDown = map[KeyType] as? String { - dropDownCaretLabel?.isHidden = false - self.hasDropDown = true - } - - // Key used to send text value to server - if let fieldKey = map[KeyFieldKey] as? String { - self.fieldKey = fieldKey - } - - switch map.stringForkey(KeyType) { - case "dropDown": - dropDownCaretLabel?.isHidden = false - self.hasDropDown = true + if previousValidity && !isValid { + feedback = errorMessage + mfTextFieldDelegate?.isInvalid?(textfield: self) + } else if !previousValidity && isValid { + clearError() + + if let mfTextFieldDelegate = mfTextFieldDelegate { + mfTextFieldDelegate.isValid?(textfield: self) + } + } + } + + func endInputing() { + + if isValid { + clearError() + separatorView?.backgroundColor = .black + } else if let errMessage = errorMessage { + feedback = errMessage + } + } + + func startEditing() { + + textField?.becomeFirstResponder() + } +} + +// MARK: - Molecular +extension TextEntryField { + + open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) + + guard let delegateObject = delegateObject, + let dictionary = json, + !dictionary.isEmpty + else { return } + + FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) + + if let enabledTextColorHex = dictionary["enabledTextColor"] as? String { + enabledTextColor = UIColor.mfGet(forHex: enabledTextColorHex) + } + + if let disabledTextColorHex = dictionary["disabledTextColor"] as? String { + disabledTextColor = UIColor.mfGet(forHex: disabledTextColorHex) + } + + switch dictionary.stringForkey(KeyType) { case "password": textField?.isSecureTextEntry = true @@ -231,7 +279,7 @@ import UIKit break } - let regex = map.stringForkey("regex") + let regex = dictionary.stringForkey("regex") if !regex.isEmpty { validationBlock = { enteredValue in @@ -241,196 +289,17 @@ import UIKit } else { defaultValidationBlock() } - } - - public func setWithMap(_ map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & TextFieldDelegate)?) { - guard let textField = textField else { return } - - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegate) - setBothTextFieldDelegates(delegate) - setWithMap(map) - } - - public func defaultValidationBlock() { - - validationBlock = { enteredValue in - return (enteredValue?.count ?? 0) > 0 - } - } - - open override func formIsEnabled() { - super.formIsEnabled() - - textField?.isUserInteractionEnabled = true - textField?.isEnabled = true - showDropDown(true) - } - - open override func formIsDisabled() { - super.formIsDisabled() - - textField?.isUserInteractionEnabled = false - textField?.isEnabled = false - self.showDropDown(false) - } - - //-------------------------------------------------- - // MARK: - Observing for change - //-------------------------------------------------- - - func valueChanged() { - - // Update label for placeholder - if !showError { - feedbackLabel?.text = "" + if let formValidationProtocol = delegateObject.formValidationProtocol { + mfTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol) } - let previousValidity = isValid + uiTextFieldDelegate = delegateObject.uiTextFieldDelegate - // If validation not set, input will always be valid - isValid = validationBlock?(text) ?? true - - if previousValidity && !isValid { - if let errMessage = errorMessage { - showErrorMessage(errMessage) - } - - if let mfTextFieldDelegate = mfTextFieldDelegate { - mfTextFieldDelegate.isInvalid?(textfield: self) - } - } else if !previousValidity && isValid { - hideError() - - if let mfTextFieldDelegate = mfTextFieldDelegate { - mfTextFieldDelegate.isValid?(textfield: self) - } + if let textField = textField { + MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) } } - - func endInputing() { - - if isValid { - hideError() - separatorView?.backgroundColor = .black - } else if let errMessage = errorMessage { - showErrorMessage(errMessage) - } - } - - func startEditing() { - - textField?.becomeFirstResponder() - showErrorDropdown(!showError) - } - - class func getEnabledTextfields(_ textFieldToDetermine: [TextEntryField]?) -> [AnyHashable]? { - - var enabledTextFields = [AnyHashable]() - - for textfield in textFieldToDetermine ?? [] { - if textfield.isEnabled { - enabledTextFields.append(textfield) - } - } - - return enabledTextFields - } -} - -// MARK: - Date Picker -extension TextEntryField { - - private func createDatePicker() { - - guard let textField = textField else { return } - - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: textField.delegate) - datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField) - - var calendar: Calendar = .current - calendar.timeZone = NSTimeZone.system - self.calendar = calendar - } - - public func inputFromDatePicker(from fromDate: Date?, to toDate: Date?, showFromDateAsDefaultInput show: Bool) { - - createDatePicker() - - if show, let fromDate = fromDate { - if let calendar = calendar, calendar.isDate(fromDate, inSameDayAs: Date()) { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - self.text = formatter.string(from: fromDate) - } - } - - datePicker?.minimumDate = fromDate - datePicker?.maximumDate = toDate - } - - public func setDatePickerFrom(_ fromDate: Date?, to toDate: Date?) { - - if let fromDate = fromDate { - if let calendar = calendar, calendar.isDate(fromDate, inSameDayAs: Date()) { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - text = formatter.string(from: fromDate) - } - } - - datePicker?.minimumDate = fromDate - datePicker?.maximumDate = toDate - datePicker?.timeZone = NSTimeZone.system - } - - public func dismissDatePicker() -> Date? { - - let pickedDate = datePicker?.date - - if let pickedDate = pickedDate { - if let calendar = calendar, calendar.isDate(pickedDate, inSameDayAs: Date()) { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - text = formatter.string(from: pickedDate) - } - } - - textField?.resignFirstResponder() - return pickedDate - } - - public func dismissPicker() { - - textField?.resignFirstResponder() - } -} - -// MARK: - Molecular -extension TextEntryField { - - override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - if let delegateObject = delegateObject { - FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) - setWithMap(json) - - if let formValidationProtocol = delegateObject.formValidationProtocol { - mfTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol) - } - - uiTextFieldDelegate = delegateObject.uiTextFieldDelegate - - if let textField = textField { - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) - } - } - } - - override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return 76 - } } // MARK: - Accessibility @@ -451,14 +320,10 @@ extension TextEntryField { var accessibilityString = accessibilityString ?? "" - if hasDropDown, let txtPickerItem = MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item") { - accessibilityString += txtPickerItem - - } else if let txtRegular = MVMCoreUIUtility.hardcodedString(withKey: "textfield_regular") { + if let txtRegular = MVMCoreUIUtility.hardcodedString(withKey: "textfield_regular") { accessibilityString += txtRegular } textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")" } - } diff --git a/MVMCoreUI/Atoms/TextFields/TextField.swift b/MVMCoreUI/Atoms/TextFields/TextField.swift deleted file mode 100644 index 3418b757..00000000 --- a/MVMCoreUI/Atoms/TextFields/TextField.swift +++ /dev/null @@ -1,799 +0,0 @@ -// -// TextField.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 10/2/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import UIKit - - -@objc public protocol TextFieldDelegate: NSObjectProtocol { - /// Called when the entered text becomes valid based on the validation block - @objc optional func isValid(textfield: TextField?) - /// Called when the entered text becomes invalid based on the validation block - @objc optional func isInvalid(textfield: TextField?) - /// Dismisses the keyboard. - @objc optional func dismissField(sender: Any?) -} - -@objcMembers open class TextField: ViewConstrainingView { - //-------------------------------------------------- - // MARK: - Outlets - //-------------------------------------------------- - - public var textFieldContainerView: UIView? - public var backgroundView: UIView? - public var textField: UITextField? - public var formLabel: Label? - public var separatorView: UIView? - public var placeholderErrorLabel: Label? - public var dashLine: DashLine? - public var dropDownCarrotLabel: UILabel? - - //-------------------------------------------------- - // MARK: - Accessories - //-------------------------------------------------- - - public weak var datePicker: UIDatePicker? - private(set) weak var toolbar: UIToolbar? - - //-------------------------------------------------- - // MARK: - Delegate Properties - //-------------------------------------------------- - - /// The delegate and block for validation. Validates if the text that the user has entered is valid or not. Checked after each change if there is a delegate. - public weak var mfTextFieldDelegate: TextFieldDelegate? { - didSet { - if mfTextFieldDelegate != nil && !observingForChanges { - observingForChanges = true - NotificationCenter.default.addObserver(self, selector: #selector(valueChanged), name: UITextField.textDidChangeNotification, object: textField) - NotificationCenter.default.addObserver(self, selector: #selector(endInputing), name: UITextField.textDidEndEditingNotification, object: textField) - NotificationCenter.default.addObserver(self, selector: #selector(startEditing), name: UITextField.textDidBeginEditingNotification, object: textField) - } else if mfTextFieldDelegate == nil && observingForChanges { - observingForChanges = false - NotificationCenter.default.removeObserver(self, name: UITextField.textDidChangeNotification, object: textField) - NotificationCenter.default.removeObserver(self, name: UITextField.textDidEndEditingNotification, object: textField) - NotificationCenter.default.removeObserver(self, name: UITextField.textDidBeginEditingNotification, object: textField) - } - } - } - - /// If you're using a MFViewController, you must set this to it - public weak var uiTextFieldDelegate: UITextFieldDelegate? { - get { - return textField?.delegate - } - set { - textField?.delegate = newValue - } - } - - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - private var calendar: Calendar? - public var pickerView: UIPickerView? - public var observingForChanges = false - public var showError = false - public var hasDropDown = false - public var isEnabled = true - - private var borderPath: UIBezierPath? - - /// Determines if a border should be drawn. - public var hideBorder = false { - didSet { - setNeedsLayout() - } - } - - public var text: String? { - get { - return textField?.text - } - set { - textField?.text = newValue - valueChanged() - } - } - - public var formText: String? { - get { - return formLabel?.text - } - set { - formLabel?.text = newValue - setAccessibilityString(newValue) - } - } - - public var placeholderTextColor: UIColor = .black - - /// Setgs placeholder text in the textField. - public var placeholder: String? { - get { - guard let attributedPlaceholder = textField?.attributedPlaceholder else { return nil } - return attributedPlaceholder.string - } - set { - guard let newPlaceholderText = newValue else { - textField?.attributedPlaceholder = nil - return - } - - textField?.attributedPlaceholder = NSAttributedString(string: newPlaceholderText, attributes: [NSAttributedString.Key.foregroundColor: placeholderTextColor]) - - if !showError { - placeholderErrorLabel?.text = (textField?.text?.count ?? 0) > 0 ? newPlaceholderText : "" - } - - setAccessibilityString(newPlaceholderText) - } - } - - public var formatter: DateFormatter = { - - let formatter = DateFormatter() - formatter.dateStyle = .medium - formatter.timeZone = NSTimeZone.system - formatter.locale = .current - formatter.formatterBehavior = .default - - return formatter - }() - - public var isValid = false - public var fieldKey: String? - public var validationBlock: ((_ enteredValue: String?) -> Bool)? { - didSet { - valueChanged() - } - } - - public var enabledTextColor: UIColor? - public var disabledTextColor: UIColor? - - public var errorMessage: String? - - //-------------------------------------------------- - // MARK: - Constraints - //-------------------------------------------------- - - public var heightConstraint: NSLayoutConstraint? - - public var dropDownCarrotWidth: NSLayoutConstraint? - - public var textContainerLeftPin: NSLayoutConstraint? - public var textContainerRightPin: NSLayoutConstraint? - - public var errorLableRightPin: NSLayoutConstraint? - public var errorLableLeftPin: NSLayoutConstraint? - - public var formLabelLeftPin: NSLayoutConstraint? - public var formLabelRightPin: NSLayoutConstraint? - - public var separatorHeightConstraint: NSLayoutConstraint? - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - - public override init(frame: CGRect) { - super.init(frame: frame) - setupView() - } - - /// Basic initializer. - public convenience init() { - self.init(frame: .zero) - } - - required public init?(coder: NSCoder) { - super.init(coder: coder) - fatalError("init(coder:) has not been implemented") - } - - /// - parameter bothDelegates: Sets both MF/UI Text Field Delegates. - public init(bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { - super.init(frame: .zero) - setupView() - setBothTextFieldDelegates(bothDelegates) - } - - /// - parameter hasDropDown: tbd - /// - parameter map: Dictionary of values to setup this TextField - /// - parameter bothDelegate: Sets both MF/UI Text Field Delegates. - public init(hasDropDown: Bool = false, map: [AnyHashable: Any]?, bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { - super.init(frame: .zero) - setupView() - dropDownCarrotLabel?.isHidden = hasDropDown - self.hasDropDown = !hasDropDown - setWithMap(map, bothDelegates: bothDelegates) - } - - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - /// Initial configuration of class and view. - override open func setupView() { - - guard subviews.isEmpty else { return } - - translatesAutoresizingMaskIntoConstraints = false - setContentHuggingPriority(.required, for: .vertical) - setContentCompressionResistancePriority(.required, for: .vertical) - backgroundColor = .clear - - let formLabel = Label() - self.formLabel = formLabel - formLabel.font = MFStyler.fontB3() - formLabel.textColor = UIColor.mfBattleshipGrey() - formLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal) - formLabel.setContentHuggingPriority(UILayoutPriority(251), for: .vertical) - formLabel.setContentCompressionResistancePriority(.required, for: .vertical) - - addSubview(formLabel) - - formLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true - formLabelLeftPin = formLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) - formLabelLeftPin?.isActive = true - formLabelRightPin = layoutMarginsGuide.trailingAnchor.constraint(equalTo: formLabel.trailingAnchor) - formLabelRightPin?.isActive = true - - let textFieldContainerView = UIView(frame: .zero) - self.textFieldContainerView = textFieldContainerView - textFieldContainerView.translatesAutoresizingMaskIntoConstraints = false - - addSubview(textFieldContainerView) - constrainTextFieldContent(textFieldContainerView) - - textFieldContainerView.topAnchor.constraint(equalTo: formLabel.bottomAnchor, constant: 4).isActive = true - textContainerLeftPin = textFieldContainerView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) - textContainerLeftPin?.isActive = true - textContainerRightPin = layoutMarginsGuide.trailingAnchor.constraint(equalTo: textFieldContainerView.trailingAnchor) - textContainerRightPin?.isActive = true - - let placeholderErrorLabel = Label() - self.placeholderErrorLabel = placeholderErrorLabel - placeholderErrorLabel.font = MFStyler.fontForTextFieldUnderLabel() - placeholderErrorLabel.textColor = .black - placeholderErrorLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal) - placeholderErrorLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal) - placeholderErrorLabel.setContentCompressionResistancePriority(.required, for: .vertical) - - addSubview(placeholderErrorLabel) - - placeholderErrorLabel.topAnchor.constraint(equalTo: textFieldContainerView.bottomAnchor).isActive = true - errorLableLeftPin = placeholderErrorLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor) - errorLableLeftPin?.isActive = true - errorLableRightPin = layoutMarginsGuide.trailingAnchor.constraint(equalTo: placeholderErrorLabel.trailingAnchor) - errorLableRightPin?.isActive = true - layoutMarginsGuide.bottomAnchor.constraint(equalTo: placeholderErrorLabel.bottomAnchor).isActive = true - - setNeedsLayout() - } - - /// Configuration logic for the text container view. - private func constrainTextFieldContent(_ parentView: UIView) { - - let backgroundView = UIView(frame: .zero) - self.backgroundView = backgroundView - backgroundView.translatesAutoresizingMaskIntoConstraints = false - - addSubview(backgroundView) - - NSLayoutConstraint.activate([ - backgroundView.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 1), - backgroundView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 1), - parentView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor, constant: 1), - parentView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor, constant: 1)]) - - let textField = UITextField(frame: .zero) - self.textField = textField - textField.translatesAutoresizingMaskIntoConstraints = false - textField.setContentCompressionResistancePriority(.required, for: .vertical) - textField.heightAnchor.constraint(equalToConstant: 24).isActive = true - textField.font = MFStyler.fontForTextField() - textField.smartQuotesType = .no - textField.smartDashesType = .no - textField.smartInsertDeleteType = .no - MFStyler.styleTextField(textField) - - addSubview(textField) - - NSLayoutConstraint.activate([ - textField.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 10), - textField.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 16), - parentView.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 10)]) - - let dropDownCarrotLabel = Label() - self.dropDownCarrotLabel = dropDownCarrotLabel - dropDownCarrotLabel.setContentHuggingPriority(UILayoutPriority(900), for: .horizontal) - dropDownCarrotLabel.setContentHuggingPriority(UILayoutPriority(251), for: .vertical) - dropDownCarrotLabel.setContentCompressionResistancePriority(UILayoutPriority(900), for: .horizontal) - dropDownCarrotLabel.isHidden = true - dropDownCarrotLabel.isUserInteractionEnabled = true - let tapOnCarrot = UITapGestureRecognizer(target: self, action: #selector(startEditing)) - dropDownCarrotLabel.addGestureRecognizer(tapOnCarrot) - - addSubview(dropDownCarrotLabel) - - dropDownCarrotLabel.topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true - dropDownCarrotLabel.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 6).isActive = true - parentView.trailingAnchor.constraint(equalTo: dropDownCarrotLabel.trailingAnchor, constant: 16).isActive = true - parentView.bottomAnchor.constraint(equalTo: dropDownCarrotLabel.bottomAnchor).isActive = true - dropDownCarrotWidth = dropDownCarrotLabel.widthAnchor.constraint(equalToConstant: 0) - dropDownCarrotWidth?.isActive = true - - let separatorView = UIView(frame: .zero) - self.separatorView = separatorView - separatorView.translatesAutoresizingMaskIntoConstraints = false - separatorView.backgroundColor = .black - - addSubview(separatorView) - - separatorHeightConstraint = separatorView.heightAnchor.constraint(equalToConstant: 1) - separatorHeightConstraint?.isActive = true - separatorView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor).isActive = true - parentView.trailingAnchor.constraint(equalTo: separatorView.trailingAnchor).isActive = true - parentView.bottomAnchor.constraint(equalTo: separatorView.bottomAnchor).isActive = true - - let dashLine = DashLine() - dashLine.translatesAutoresizingMaskIntoConstraints = false - dashLine.backgroundColor = .white - dashLine.isHidden = true - - addSubview(dashLine) - - NSLayoutConstraint.activate([ - dashLine.centerYAnchor.constraint(equalTo: separatorView.centerYAnchor), - dashLine.centerXAnchor.constraint(equalTo: separatorView.centerXAnchor), - dashLine.topAnchor.constraint(equalTo: separatorView.topAnchor), - dashLine.leadingAnchor.constraint(equalTo: separatorView.leadingAnchor)]) - } - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - - formLabel?.updateView(size) - placeholderErrorLabel?.font = MFStyler.fontForTextFieldUnderLabel() - if let textField = textField { - MFStyler.styleTextField(textField) - } - dashLine?.updateView(size) - - layoutIfNeeded() - } - - deinit { - mfTextFieldDelegate = nil - uiTextFieldDelegate = nil - } - - open override func draw(_ rect: CGRect) { - super.draw(rect) - - borderPath?.removeAllPoints() - - if !hideBorder, let frame = textFieldContainerView?.frame { - - borderPath = UIBezierPath() - borderPath?.move(to: CGPoint(x: frame.origin.x, y: frame.origin.y + frame.size.height)) - borderPath?.addLine(to: CGPoint(x: frame.origin.x, y: frame.origin.y)) - borderPath?.addLine(to: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y)) - borderPath?.addLine(to: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y + frame.size.height)) - borderPath?.lineWidth = 1 - - let strokeColor = showError ? UIColor.mfPumpkin() : UIColor.mfSilver() - strokeColor.setStroke() - - borderPath?.stroke() - } - } - - //-------------------------------------------------- - // MARK: - Methods - //-------------------------------------------------- - - func showErrorDropdown(_ show: Bool) { - - DispatchQueue.main.async { [weak self] in - - self?.showError = show - self?.separatorHeightConstraint?.constant = show ? 4 : 1 - self?.separatorView?.backgroundColor = show ? UIColor.mfPumpkin() : .black - self?.setNeedsDisplay() - self?.layoutIfNeeded() - } - } - - func showErrorMessage(_ errorMessage: String?) { - - guard isEnabled else { return } - - DispatchQueue.main.async { [weak self] in - - self?.separatorHeightConstraint?.constant = 4 - self?.showError = true - self?.separatorView?.backgroundColor = UIColor.mfPumpkin() - self?.placeholderErrorLabel?.text = errorMessage - self?.placeholderErrorLabel?.numberOfLines = 0 - self?.textField?.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message") ?? "", self?.textField?.text ?? "", self?.errorMessage ?? "") - self?.setNeedsDisplay() - self?.layoutIfNeeded() - self?.showErrorDropdown(self?.showError ?? false) - } - } - - public func hideError() { - - DispatchQueue.main.async { [weak self] in - self?.separatorHeightConstraint?.constant = 1 - self?.separatorView?.backgroundColor = .black - self?.layoutIfNeeded() - self?.showError = false - self?.placeholderErrorLabel?.textColor = .black - self?.placeholderErrorLabel?.text = "" - self?.textField?.accessibilityValue = nil - self?.setNeedsDisplay() - self?.layoutIfNeeded() - } - } - - public func setBothTextFieldDelegates(_ delegate: (UITextFieldDelegate & TextFieldDelegate)?) { - - mfTextFieldDelegate = delegate - uiTextFieldDelegate = delegate - } - - public func setWithMap(_ map: [AnyHashable: Any]?) { - - guard let map = map, !map.isEmpty else { return } - - if let formText = map[KeyLabel] as? String { - self.formText = formText - } - - if let text = map[KeyValue] as? String { - self.text = text - } - - if let text = map[KeyDisable] as? String, text.isEqual(StringY) || map.boolForKey(KeyDisable) { - isEnabled(false) - } - - if let errMessage = map[KeyErrorMessage] as? String { - self.errorMessage = errMessage - } - - if let hideBorder = map["hideBorder"] as? Bool { - self.hideBorder = hideBorder - } - - // Key used to send text value to server - if let fieldKey = map[KeyFieldKey] as? String { - self.fieldKey = fieldKey - } - - switch map.stringForkey(KeyType) { - case "dropDown": - dropDownCarrotLabel?.isHidden = false - self.hasDropDown = true - - case "password": - textField?.isSecureTextEntry = true - - case "number": - textField?.keyboardType = .numberPad - - case "email": - textField?.keyboardType = .emailAddress - - default: - break - } - - let regex = map.stringForkey("regex") - - if !regex.isEmpty { - validationBlock = { enteredValue in - guard let value = enteredValue else { return false } - return MVMCoreUIUtility.validate(value, withRegularExpression: regex) - } - } else { - defaultValidationBlock() - } - } - - public func setWithMap(_ map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & TextFieldDelegate)?) { - - guard let textField = textField else { return } - - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegate) - setBothTextFieldDelegates(delegate) - setWithMap(map) - } - - public func defaultValidationBlock() { - - validationBlock = { enteredValue in - return (enteredValue?.count ?? 0) > 0 - } - } - - open override func setLeftPinConstant(_ constant: CGFloat) { - - textContainerLeftPin?.constant = constant - errorLableLeftPin?.constant = constant - formLabelLeftPin?.constant = constant - } - - open override func setRightPinConstant(_ constant: CGFloat) { - - textContainerRightPin?.constant = constant - errorLableRightPin?.constant = constant - formLabelRightPin?.constant = constant - } - - public func showDropDown(_ show: Bool) { - - if hasDropDown { - dropDownCarrotLabel?.isHidden = !show - dropDownCarrotWidth?.isActive = !show - setNeedsLayout() - layoutIfNeeded() - } - } - - public func isEnabled(_ enable: Bool) { - - // Set outside the dispatch so that registerAnimations can know about it - isEnabled = enable - - DispatchQueue.main.async { [weak self] in - - self?.isUserInteractionEnabled = enable - self?.textField?.isUserInteractionEnabled = enable - self?.textField?.isEnabled = enable - - if enable { - self?.textField?.textColor = self?.enabledTextColor ?? .black - self?.formLabel?.textColor = UIColor.mfBattleshipGrey() - self?.placeholderErrorLabel?.textColor = .black - self?.separatorView?.backgroundColor = (self?.showError ?? false) ? UIColor.mfPumpkin() : .black - self?.showDropDown(true) - - } else { - self?.textField?.textColor = self?.disabledTextColor ?? UIColor.mfSilver() - self?.formLabel?.textColor = UIColor.mfSilver() - self?.placeholderErrorLabel?.textColor = UIColor.mfSilver() - self?.showDropDown(false) - self?.hideError() // Should not have error if the field is disabled - self?.separatorView?.backgroundColor = UIColor.mfSilver() - } - } - } - - public func showLabel(_ show: Bool) { - - placeholderErrorLabel?.isHidden = !show - } - - public func dashSeperatorView(_ dash: Bool) { - - // Never hide seperator view because it could be possiblely used by other classes for positioning - dashLine?.isHidden = !dash - separatorView?.backgroundColor = dash ? .clear : .black - } - - //-------------------------------------------------- - // MARK: - Observing for change - //-------------------------------------------------- - - func valueChanged() { - - // Update label for placeholder - if !showError { - placeholderErrorLabel?.text = "" - } - - let previousValidity = isValid - - // If validation not set, input will always be valid - isValid = validationBlock?(text) ?? true - - if previousValidity && !isValid { - if let errMessage = errorMessage { - showErrorMessage(errMessage) - } - - if let mfTextFieldDelegate = mfTextFieldDelegate { - mfTextFieldDelegate.isInvalid?(textfield: self) - } - } else if !previousValidity && isValid { - hideError() - - if let mfTextFieldDelegate = mfTextFieldDelegate { - mfTextFieldDelegate.isValid?(textfield: self) - } - } - } - - func endInputing() { - - if isValid { - hideError() - separatorView?.backgroundColor = .black - } else if let errMessage = errorMessage { - showErrorMessage(errMessage) - } - } - - func startEditing() { - - textField?.becomeFirstResponder() - showErrorDropdown(!showError) - } - - class func getEnabledTextfields(_ textFieldToDetermine: [MFTextField]?) -> [AnyHashable]? { - - var enabledTextFields = [AnyHashable]() - - for textfield in textFieldToDetermine ?? [] { - if textfield.isEnabled { - enabledTextFields.append(textfield) - } - } - - return enabledTextFields - } -} - -// MARK: - Date Picker -extension TextField { - - private func createDatePicker() { - - guard let textField = textField else { return } - - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: textField.delegate) - datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField) - - var calendar: Calendar = .current - calendar.timeZone = NSTimeZone.system - self.calendar = calendar - } - - public func inputFromDatePicker(from fromDate: Date?, to toDate: Date?, showFromDateAsDefaultInput show: Bool) { - - createDatePicker() - - if show, let fromDate = fromDate { - if let calendar = calendar, calendar.isDate(fromDate, inSameDayAs: Date()) { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - self.text = formatter.string(from: fromDate) - } - } - - datePicker?.minimumDate = fromDate - datePicker?.maximumDate = toDate - } - - public func setDatePickerFrom(_ fromDate: Date?, to toDate: Date?) { - - if let fromDate = fromDate { - if let calendar = calendar, calendar.isDate(fromDate, inSameDayAs: Date()) { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - text = formatter.string(from: fromDate) - } - } - - datePicker?.minimumDate = fromDate - datePicker?.maximumDate = toDate - datePicker?.timeZone = NSTimeZone.system - } - - public func dismissDatePicker() -> Date? { - - let pickedDate = datePicker?.date - - if let pickedDate = pickedDate { - if let calendar = calendar, calendar.isDate(pickedDate, inSameDayAs: Date()) { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - text = formatter.string(from: pickedDate) - } - } - - textField?.resignFirstResponder() - return pickedDate - } - - public func dismissPicker() { - - textField?.resignFirstResponder() - } -} - -// MARK: - Molecular -extension TextField { - - override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { - super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData) - - if let delegateObject = delegateObject { - FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) - setWithMap(json) - - if let formValidationProtocol = delegateObject.formValidationProtocol { - mfTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol) - } - - uiTextFieldDelegate = delegateObject.uiTextFieldDelegate - - if let textField = textField { - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) - } - } - } - - override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { - return 76 - } -} - -// MARK: - Accessibility -extension TextField { - - func pushAccessibilityNotification() { - - DispatchQueue.main.async { [weak self] in - UIAccessibility.post(notification: .layoutChanged, argument: self?.textField) - } - } - - /** - Adding missing accessibilityLabel value - if we have some value in accessibilityLabel, - then only can append regular and picker item - */ - func setAccessibilityString(_ accessibilityString: String?) { - - guard let textField = textField else { return } - - var accessibilityString = accessibilityString ?? "" - - if hasDropDown, let txtPickerItem = MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item") { - accessibilityString += txtPickerItem - - } else if let txtRegular = MVMCoreUIUtility.hardcodedString(withKey: "textfield_regular") { - accessibilityString += txtRegular - } - - textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")" - } - -} - -// MARK: - Form Validation -extension TextField: FormValidationProtocol { - - public func isValidField() -> Bool { - return isValid - } - - public func formFieldName() -> String? { - return fieldKey - } - - public func formFieldValue() -> Any? { - return text - } -}