diff --git a/MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift b/MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift index f0a7a368..4838e459 100644 --- a/MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/DateDropdownEntryField.swift @@ -22,7 +22,6 @@ import UIKit return calendar }() - // TODO: Pull this out into Styler or some class akin to it. public var formatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .medium @@ -38,7 +37,6 @@ import UIKit @objc public override init(frame: CGRect) { super.init(frame: frame) - setup() } @objc public convenience init() { @@ -58,7 +56,8 @@ import UIKit // MARK: - Methods //-------------------------------------------------- - @objc private func setup() { + public override func setupFieldContainerContent(_ container: UIView) { + super.setupFieldContainerContent(container) datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField) datePicker?.addTarget(self, action: #selector(pickerValueChanged), for: .valueChanged) @@ -81,7 +80,7 @@ import UIKit let pickedDate = datePicker?.date setTextWith(date: pickedDate) - textField.resignFirstResponder() + resignFirstResponder() return pickedDate } diff --git a/MVMCoreUI/Atoms/TextFields/DigitBox.swift b/MVMCoreUI/Atoms/TextFields/DigitBox.swift index dc2ad1c4..ffc1fa01 100644 --- a/MVMCoreUI/Atoms/TextFields/DigitBox.swift +++ b/MVMCoreUI/Atoms/TextFields/DigitBox.swift @@ -34,6 +34,9 @@ import UIKit private var previousSize: CGFloat = 0.0 + // Default dimensions of the DigitBox + static let size: CGSize = CGSize(width: 39, height: 44) + //-------------------------------------------------- // MARK: - Computed Properties //-------------------------------------------------- @@ -97,7 +100,7 @@ import UIKit guard constraints.isEmpty else { return } translatesAutoresizingMaskIntoConstraints = false - backgroundColor = .white + backgroundColor = .clear addSubview(digitField) digitField.delegate = self @@ -112,9 +115,9 @@ import UIKit digitField.centerYAnchor.constraint(equalTo: centerYAnchor), digitField.centerXAnchor.constraint(equalTo: centerXAnchor)]) - widthConstraint = widthAnchor.constraint(equalToConstant: 39) + widthConstraint = widthAnchor.constraint(equalToConstant: DigitBox.size.width) widthConstraint?.isActive = true - heightConstraint = heightAnchor.constraint(equalToConstant: 44) + heightConstraint = heightAnchor.constraint(equalToConstant: DigitBox.size.height) heightConstraint?.isActive = true if let bottomBar = bottomBar { diff --git a/MVMCoreUI/Atoms/TextFields/EntryField.swift b/MVMCoreUI/Atoms/TextFields/EntryField.swift index 7690dd9b..c790483f 100644 --- a/MVMCoreUI/Atoms/TextFields/EntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/EntryField.swift @@ -105,7 +105,7 @@ import UIKit } } } - + /// Sets the text of titleLabel public var title: String? { get { return titleLabel.text } @@ -177,10 +177,10 @@ import UIKit /// Initial configuration of class and view. @objc final public override func setupView() { + super.setupView() guard subviews.isEmpty else { return } - translatesAutoresizingMaskIntoConstraints = false isAccessibilityElement = false setContentCompressionResistancePriority(.required, for: .vertical) accessibilityElements = [titleLabel, feedbackLabel] diff --git a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift b/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift index 67cdbb9a..6911c8c7 100644 --- a/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/MdnEntryField.swift @@ -97,23 +97,22 @@ import MVMCore return MVMCoreUIUtility.validateInternationalMDNString(MDN) } - @objc public func validate() -> Bool { + @objc public override func validateTextField() -> Bool { - if !shouldValidateMDN { - let isValid = hasValidMDN() + guard !shouldValidateMDN else { return true } + + let isValid = hasValidMDN() + + if isValid { + showError = false - if isValid { - showError = false - } else { - errorMessage = errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") - showError = true - UIAccessibility.post(notification: .layoutChanged, argument: textField) - } - - return isValid + } else { + errorMessage = errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message") + showError = true + UIAccessibility.post(notification: .layoutChanged, argument: textField) } - return true + return isValid } @objc public func getContacts(_ sender: Any?) { @@ -183,7 +182,7 @@ import MVMCore proprietorTextDelegate?.textFieldDidEndEditing?(textField) - if validate() && isNationalMDN { + if validateTextField() && isNationalMDN { textField.text = MVMCoreUIUtility.formatMdn(textField.text) } } diff --git a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift index 3c2b4184..0f85c47f 100644 --- a/MVMCoreUI/Atoms/TextFields/TextEntryField.swift +++ b/MVMCoreUI/Atoms/TextFields/TextEntryField.swift @@ -42,7 +42,13 @@ import UIKit /// Set enabled and disabled colors to be utilized when setting this texfield's isEnabled property. public var textColor: (enabled: UIColor?, disabled: UIColor?) = (.black, .mfSilver()) - public var observingForChange: Bool = false + private var observingForChange: Bool = false + + /// Validate on each entry in the textField. Default: false + public var validateEachCharacter: Bool = false + + /// Validate when user resigns editing. Default: true + public var validateWhenDoneEditing: Bool = true //-------------------------------------------------- // MARK: - Computed Properties @@ -75,7 +81,6 @@ import UIKit get { return textField.text } set { textField.text = newValue - valueChanged() } } @@ -101,7 +106,7 @@ import UIKit // 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. + /// The delegate and block for validation. Validates if the text that the user has entered. public weak var observingTextFieldDelegate: ObservingTextFieldDelegate? { didSet { if observingTextFieldDelegate != nil && !observingForChange { @@ -193,33 +198,30 @@ import UIKit // MARK: - Observing for Change (TextFieldDelegate) //-------------------------------------------------- + public func defaultValidationBlock() { + + validationBlock = { enteredValue in + guard let enteredValue = enteredValue else { return false } + return enteredValue.count > 0 + } + } + + @discardableResult @objc override open func resignFirstResponder() -> Bool { + if validateWhenDoneEditing { + validateTextField() + } + textField.resignFirstResponder() isSelected = false return true } - @objc func dismissFieldInput(_ sender: Any?) { - - _ = resignFirstResponder() - } - - public func defaultValidationBlock() { - - validationBlock = { enteredValue in - return (enteredValue?.count ?? 0) > 0 - } - } - - /// Executes on UITextField.textDidChangeNotification - @objc func valueChanged() { - - if !showError { - feedback = "" - } + /// Validates the text of the entry field. + @discardableResult + @objc public func validateTextField() -> Bool { - // If validation not set, input will always be valid isValid = validationBlock?(text) ?? true if !isValid { @@ -227,22 +229,10 @@ import UIKit observingTextFieldDelegate?.isInvalid?(textfield: self) } else if isValid { - if showError == true { - showError = false - } - isSelected = true observingTextFieldDelegate?.isValid?(textfield: self) } - } - - /// Executes on UITextField.textDidEndEditingNotification - @objc func endInputing() { - if isValid { - showError = false - entryFieldContainer.bottomBar?.backgroundColor = UIColor.black.cgColor - - } + return isValid } /// Executes on UITextField.textDidBeginEditingNotification @@ -251,9 +241,33 @@ import UIKit isSelected = true textField.becomeFirstResponder() } + + /// Executes on UITextField.textDidChangeNotification (every character entry) + @objc func valueChanged() { + + guard validateEachCharacter else { return } + + validateTextField() + } + + /// Executes on UITextField.textDidEndEditingNotification + @objc func endInputing() { + + if isValid { + showError = false + entryFieldContainer.bottomBar?.backgroundColor = UIColor.black.cgColor + } + + resignFirstResponder() + } + + @objc func dismissFieldInput(_ sender: Any?) { + + resignFirstResponder() + } } -// MARK: - Molecular +// MARK: - MVMCoreUIMoleculeViewProtocol extension TextEntryField { @objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { diff --git a/MVMCoreUI/BaseClasses/View.swift b/MVMCoreUI/BaseClasses/View.swift index 8ae475b9..515dc0eb 100644 --- a/MVMCoreUI/BaseClasses/View.swift +++ b/MVMCoreUI/BaseClasses/View.swift @@ -44,7 +44,7 @@ import UIKit } } -/// MARK:- MVMCoreViewProtocol +// MARK:- MVMCoreViewProtocol extension View: MVMCoreViewProtocol { open func updateView(_ size: CGFloat) {} @@ -56,7 +56,7 @@ extension View: MVMCoreViewProtocol { } } -/// MARK:- MVMCoreUIMoleculeViewProtocol +// MARK:- MVMCoreUIMoleculeViewProtocol extension View: MVMCoreUIMoleculeViewProtocol { open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { diff --git a/MVMCoreUI/Containers/views/EntryFieldContainer.swift b/MVMCoreUI/Containers/views/EntryFieldContainer.swift index fc04b0bf..00c391a6 100644 --- a/MVMCoreUI/Containers/views/EntryFieldContainer.swift +++ b/MVMCoreUI/Containers/views/EntryFieldContainer.swift @@ -33,8 +33,9 @@ import UIKit private(set) var fieldState: FieldState = .original { didSet (oldState) { // Will not update if new state is the same as old. - guard fieldState != oldState else { return } - fieldState.setStateUI(for: self) + if fieldState != oldState { + fieldState.setStateUI(for: self) + } } } @@ -100,8 +101,8 @@ import UIKit _isLocked = false _isEnabled = true - if selected && showError { - fieldState = .selectedError + if _showError { + fieldState = selected ? .selectedError : .error } else { fieldState = selected ? .selected : .original } @@ -174,6 +175,7 @@ import UIKit _showError = false subviews.forEach { $0.removeFromSuperview() } + borderPath.removeAllPoints() } //-------------------------------------------------- @@ -277,10 +279,10 @@ import UIKit layoutIfNeeded() } } - - //-------------------------------------------------- - // MARK: - Molecular - //-------------------------------------------------- +} + +// MARK:- MVMCoreUIMoleculeViewProtocol +extension EntryFieldContainer { override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)