From 403f0b261b613b4b0a0def3af32c59e362de1a44 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Mon, 14 Oct 2019 15:24:48 -0400 Subject: [PATCH] Major swiftification of MFTextField to TextField. --- MVMCoreUI/Atoms/TextFields/TextField.swift | 741 ++++++++++-------- MVMCoreUI/Atoms/Views/DashLine.swift | 21 +- .../MVMCoreUIMoleculeMappingObject.m | 2 +- 3 files changed, 436 insertions(+), 328 deletions(-) diff --git a/MVMCoreUI/Atoms/TextFields/TextField.swift b/MVMCoreUI/Atoms/TextFields/TextField.swift index 09e170ca..fb4867bf 100644 --- a/MVMCoreUI/Atoms/TextFields/TextField.swift +++ b/MVMCoreUI/Atoms/TextFields/TextField.swift @@ -9,7 +9,7 @@ import UIKit -@objc public protocol MFTextFieldDelegate: NSObjectProtocol { +@objc public protocol TextFieldDelegate: NSObjectProtocol { /// Called when the entered text becomes valid based on the validation block @objc optional func entryIsValid(_ textfield: TextField?) /// Called when the entered text becomes invalid based on the validation block @@ -23,66 +23,141 @@ import UIKit // MARK: - Outlets //-------------------------------------------------- - var textFieldContainerView: UIView? - var backgroundView: UIView? - var textField: UITextField? - var formLabel: Label? - var separatorView: UIView? - var view: UIView? + 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 view: UIView? - /// If you're using a MFViewController, you must set this to it - weak var mfTextFieldDelegate: MFTextFieldDelegate? - - // 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. - var uiTextFieldDelegate: UITextFieldDelegate? { - get { - return textField?.delegate - } - set (newValue) { - textField?.delegate = newValue - } - } + public var dashLine: DashLine? + public var dropDownCarrotLabel: UILabel? + public weak var datePicker: UIDatePicker? + private(set) weak var toolbar: UIToolbar? //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - weak var label: UILabel? + private var _mfTextFieldDelegate: TextFieldDelegate? + private var _uiTextFieldDelegate: UITextFieldDelegate? - weak var pickerView: UIPickerView? - - var observingForChanges = false - var errorShowing = false - var hasDropDown = false + /// 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? { + get { + return _mfTextFieldDelegate + } + set { + _mfTextFieldDelegate = newValue + 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) + } + } + } - var dashLine: DashLine? - var dropDownCarrotLabel: UILabel? - var dropDownCarrotWidth: NSLayoutConstraint? + // If you're using a MFViewController, you must set this to it + public weak var uiTextFieldDelegate: UITextFieldDelegate? { + get { + return textField?.delegate + } + set { + _uiTextFieldDelegate = newValue + textField?.delegate = uiTextFieldDelegate + } + } - var text: String? - var formText: String? - var fieldKey: String? - var placeholder: String? + public weak var pickerView: UIPickerView? - var enabled = false - - var hideBorder = false + public var observingForChanges = false + public var errorShowing = false + public var hasDropDown = false - weak var datePicker: UIDatePicker? - private(set) weak var toolbar: UIToolbar? + public var text: String? { + get { + return textField?.text + } + set { + textField?.text = newValue + valueChanged() + } + } - var formatter: DateFormatter? + public var formText: String? { + get { + return formLabel?.text + } + set { + formLabel?.text = newValue + setAccessibilityString(newValue) + } + } - var valid = false - var validationBlock: ((_ enteredValue: String?) -> Bool)? - - var customEnabledTextColor: UIColor? - var customDisabledTextColor: UIColor? - var errMessage: String? - var editCompleteAction: ((_ text: String?) -> ())? + public var fieldKey: String? + + public var placeholderTextColor: UIColor = .black + + public var placeholder: String? { + get { + guard let attributedPlaceholder = textField?.attributedPlaceholder else { return nil } + return attributedPlaceholder.string + } + set { + guard let newPlaceholderText = newValue else { return } + + textField?.attributedPlaceholder = NSAttributedString(string: newPlaceholderText, attributes: [NSAttributedString.Key.foregroundColor: placeholderTextColor]) + + if !errorShowing { + placeholderErrorLabel?.text = (textField?.text?.count ?? 0) > 0 ? newPlaceholderText : "" + } + + setAccessibilityString(newPlaceholderText) + } + } + + public var enabled = false + + public var hideBorder = false + + 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 + + private var _validationBlock: ((_ enteredValue: String?) -> Bool)? + public var validationBlock: ((_ enteredValue: String?) -> Bool)? { + get { + return _validationBlock + } + set { + _validationBlock = newValue + valueChanged() + } + } + + public var customEnabledTextColor: UIColor? + public var customDisabledTextColor: UIColor? + + public var errMessage: String? + public var editCompleteAction: ((_ text: String?) -> ())? - private var customPlaceHolderColor: UIColor? private var borderPath: UIBezierPath? private var calendar: Calendar? @@ -90,23 +165,29 @@ import UIKit // MARK: - Constraints //-------------------------------------------------- - var textContainerLeftPin: NSLayoutConstraint? - var errorLableLeftPin: NSLayoutConstraint? - var formLabelLeftPin: NSLayoutConstraint? - var textContainerRightPin: NSLayoutConstraint? - var errorLableRightPin: NSLayoutConstraint? - var separatorHeightConstraint: NSLayoutConstraint? - var heightConstraint: NSLayoutConstraint? - var formLabelRightPin: NSLayoutConstraint? + 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: - Initializations + // MARK: - Initializers //-------------------------------------------------- public override init(frame: CGRect) { super.init(frame: frame) - translatesAutoresizingMaskIntoConstraints = false + setupView() } required public init?(coder: NSCoder) { @@ -116,40 +197,33 @@ import UIKit public convenience init() { self.init(frame: .zero) - + hasDropDown = false } - - public convenience init(withBothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { - self.init(frame: .zero) - - mfTextFieldDelegate = delegate - uiTextFieldDelegate = delegate - } - public convenience init(withMap map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { + public convenience init(with bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { self.init(frame: .zero) - setWithMap(map, bothDelegates: delegate) + mfTextFieldDelegate = bothDelegates + uiTextFieldDelegate = bothDelegates } - - public convenience init(mfTextFieldForDropDown: Bool) { + + public convenience init(map: [AnyHashable: Any]?, with bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { + self.init(frame: .zero) + + setWithMap(map, bothDelegates: bothDelegates) + } + + public convenience init(dropDownWithMap map: [AnyHashable: Any]?, bothDelegates: (UITextFieldDelegate & TextFieldDelegate)?) { self.init(frame: .zero) dropDownCarrotLabel?.isHidden = false hasDropDown = true - } - - public convenience init(mfTextFieldForDropDownWithMap map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { - self.init(frame: .zero) - - dropDownCarrotLabel?.isHidden = false - hasDropDown = true - setWithMap(map, bothDelegates: delegate) + setWithMap(map, bothDelegates: bothDelegates) } //-------------------------------------------------- - // MARK: - Lifecycle + // MARK: - Setup //-------------------------------------------------- override open func setupView() { @@ -157,47 +231,124 @@ import UIKit guard subviews.isEmpty else { return } translatesAutoresizingMaskIntoConstraints = false - + setContentHuggingPriority(.required, for: .vertical) + setContentCompressionResistancePriority(.required, for: .vertical) backgroundColor = .clear - - let view = views?.first as? UIView - view?.translatesAutoresizingMaskIntoConstraints = false - view?.setContentHuggingPriority(.required, for: .vertical) - view?.setContentCompressionResistancePriority(.required, for: .vertical) - self.view = view - view?.frame = frame - - if let view = view { - addSubview(view) - } - pinView(toSuperView: view) - - textField?.font = MFStyler.fontForTextField() - translatesAutoresizingMaskIntoConstraints = false - - formLabel?.font = MFStyler.fontB3() - formLabel?.textColor = UIColor.mfBattleshipGrey() - - label?.font = MFStyler.fontForTextFieldUnderLabel() - label?.textColor = UIColor.black - - dropDownCarrotLabel?.isHidden = true - separatorView?.backgroundColor = UIColor.black - dashLine?.backgroundColor = UIColor.white - dashLine?.isHidden = true - MFStyler.styleTextField(textField!) - - dropDownCarrotLabel?.isUserInteractionEnabled = true - let tapOnCarrot = UITapGestureRecognizer(target: self, action: #selector(startEditing)) - dropDownCarrotLabel?.addGestureRecognizer(tapOnCarrot) enabled = true - // Disable SmartQuotes - if #available(iOS 11.0, *) { - textField?.smartQuotesType = .no - textField?.smartDashesType = .no - textField?.smartInsertDeleteType = .no - } + let formLabel = Label() + self.formLabel = formLabel + formLabel.font = MFStyler.fontB3() + formLabel.textColor = UIColor.mfBattleshipGrey() + + 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) + constrainTextContainerContent() + + 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 + + addSubview(placeholderErrorLabel) + + placeholderErrorLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 15).isActive = true + 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 + } + + private func constrainTextContainerContent() { + + guard let parentView = textFieldContainerView else { return } + + 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.font = MFStyler.fontForTextField() + textField.smartQuotesType = .no + textField.smartDashesType = .no + textField.smartInsertDeleteType = .no + MFStyler.styleTextField(textField) + + addSubview(textField) + + textField.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 10).isActive = true + textField.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 16).isActive = true + parentView.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 10).isActive = true + + let dropDownCarrotLabel = Label() + self.dropDownCarrotLabel = dropDownCarrotLabel + dropDownCarrotLabel.isHidden = true + dropDownCarrotLabel.isUserInteractionEnabled = true + let tapOnCarrot = UITapGestureRecognizer(target: self, action: #selector(startEditing)) + dropDownCarrotLabel.addGestureRecognizer(tapOnCarrot) + + addSubview(dropDownCarrotLabel) + + textField.topAnchor.constraint(equalTo: parentView.topAnchor, constant: 10).isActive = true + textField.leadingAnchor.constraint(equalTo: textField.leadingAnchor, 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.backgroundColor = .white + dashLine.isHidden = true + + addSubview(dashLine) + + dashLine.centerYAnchor.constraint(equalTo: separatorView.centerYAnchor).isActive = true + dashLine.centerXAnchor.constraint(equalTo: separatorView.centerXAnchor).isActive = true + dashLine.topAnchor.constraint(equalTo: separatorView.topAnchor).isActive = true + dashLine.leadingAnchor.constraint(equalTo: separatorView.leadingAnchor).isActive = true + separatorView.bottomAnchor.constraint(equalTo: dashLine.bottomAnchor).isActive = true } open override func updateView(_ size: CGFloat) { @@ -206,10 +357,10 @@ import UIKit DispatchQueue.main.async { [weak self] in self?.formLabel?.updateView(size) - self?.label?.font = MFStyler.fontForTextFieldUnderLabel() -// let textFieldView = { - MFStyler.styleTextField(self!.textField!) -// } + self?.placeholderErrorLabel?.font = MFStyler.fontForTextFieldUnderLabel() + if let textField = self?.textField { + MFStyler.styleTextField(textField) + } self?.dashLine?.updateView(size) } } @@ -224,8 +375,7 @@ import UIKit borderPath?.removeAllPoints() - if !hideBorder { - let frame = textFieldContainerView!.frame + if !hideBorder, let frame = textFieldContainerView?.frame { borderPath = UIBezierPath() borderPath?.move(to: CGPoint(x: frame.origin.x, y: frame.origin.y + frame.size.height)) @@ -236,6 +386,7 @@ import UIKit let strokeColor = errorShowing ? UIColor.mfPumpkin() : UIColor.mfSilver() strokeColor.setStroke() + borderPath?.stroke() } } @@ -244,64 +395,61 @@ import UIKit // MARK: - Methods //-------------------------------------------------- - func placeholder() -> String? { - return textField.attributedPlaceholder.string - } - - func text() -> String? { - return textField.text() - } - - // MARK: - Utilities func createDatePicker() { - //tool bar - MVMCoreUICommonViewsUtility.addDismissToolbar(textField!, delegate: textField!.delegate) - //date picker - datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField!) + guard let textField = textField else { return } - var calendar = Calendar.current + MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: textField.delegate) + datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField) + + var calendar: Calendar = .current calendar.timeZone = NSTimeZone.system self.calendar = calendar } func inputFromDatePicker(from fromDate: Date?, to toDate: Date?, showFromDateAsDefaultInput show: Bool) { + createDatePicker() - if show { - if let fromDate = fromDate { - if (calendar?.isDate(fromDate, inSameDayAs: Date()))! { - text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") - } else { - self.text = formatter.string(from: fromDate) - } + + 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 } func setDatePickerFrom(_ fromDate: Date?, to toDate: Date?) { + if let fromDate = fromDate { - if (calendar?.isDate(fromDate, inSameDayAs: Date()))! { + if let calendar = calendar, calendar.isDate(fromDate, inSameDayAs: Date()) { text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") } else { - self.text = formatter.string(from: fromDate) + self.text = formatter?.string(from: fromDate) } } + datePicker?.minimumDate = fromDate datePicker?.maximumDate = toDate datePicker?.timeZone = NSTimeZone.system } func dismissDatePicker() -> Date? { - var pickedDate = datePicker!.date - if let pickedDatei = pickedDate { - if calendar.isDate(pickedDatei, inSameDayAs: Date()) { + + var pickedDate = datePicker?.date + + if let pickedDate = pickedDate { + if let calendar = calendar, calendar.isDate(pickedDate, inSameDayAs: Date()) { text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") } else { - self.text = formatter.string(from: pickedDatei) + self.text = formatter?.string(from: pickedDate) } } + textField?.resignFirstResponder() return pickedDate } @@ -313,13 +461,16 @@ import UIKit func setErrorMessage(_ errorMessage: String?) { DispatchQueue.main.async { [weak self] in - if let enabled = self?.enabled { + + if let enabled = self?.enabled, enabled { self?.separatorHeightConstraint?.constant = 4 self?.errorShowing = true self?.separatorView?.backgroundColor = UIColor.mfPumpkin() - self?.label?.text = errorMessage - self?.label?.numberOfLines = 0 - self?.textField?.accessibilityValue() = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message"), self.textField.text() ?? "", errorMessage ?? "") + self?.placeholderErrorLabel?.text = errorMessage + self?.placeholderErrorLabel?.numberOfLines = 0 + self?.textField?.accessibilityValue = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message") ?? "", + self?.textField?.text ?? "", + errorMessage ?? "") self?.setNeedsDisplay() self?.layoutIfNeeded() } @@ -327,96 +478,47 @@ import UIKit } func pushAccessibilityNotification() { + DispatchQueue.main.async { [weak self] in - UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: self?.textField) + UIAccessibility.post(notification: .layoutChanged, argument: self?.textField) } } func hideError() { DispatchQueue.main.async { [weak self] in - self?.separatorHeightConstraint!.constant = 1 + self?.separatorHeightConstraint?.constant = 1 self?.separatorView?.backgroundColor = .black self?.layoutIfNeeded() self?.errorShowing = false - self?.label?.textColor = .black - self?.label?.text = "" + self?.placeholderErrorLabel?.textColor = .black + self?.placeholderErrorLabel?.text = "" self?.textField?.accessibilityValue = nil self?.setNeedsDisplay() self?.layoutIfNeeded() } } - func setPlaceholder(_ placeholder: String?, with color: UIColor?) { - customPlaceHolderColor = color - - if placeholder != nil { - if let color = color { - textField!.attributedPlaceholder = NSAttributedString(string: placeholder ?? "", attributes: [ - NSAttributedString.Key.foregroundColor: color - ]) - } - } - if textField?.text.length > 0 && !errorShowing { - label?.text = placeholder - } else if !errorShowing { - label?.text = "" - } - setAccessibilityString(placeholder) - } - + /** + Adding missing accessibilityLabel value + if we have some value in accessibilityLabel, + then only can append regular and picker item + */ func setAccessibilityString(_ accessibilityString: String?) { - var accessibilityString = accessibilityString - // adding missing accessibilityLabel value - // if we have some value in accessibilityLabel, - // then only can append regular and picker item - if hasDropDown { - // MFDLog(@"Label: %@", self.textField.accessibilityLabel); - accessibilityString = accessibilityString ?? "" + (MVMCoreUIUtility.hardcodedString(withKey: "textfield_picker_item")!) - // MFDLog(@"Label: %@", self.textField.accessibilityLabel); - } else { - accessibilityString = accessibilityString ?? "" + (MVMCoreUIUtility.hardcodedString(withKey: "textfield_regular")!) - // MFDLog(@"Label: %@", self.textField.accessibilityLabel); + + 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"))" + textField.accessibilityLabel = "\(accessibilityString) \(textField.isEnabled ? "" : MVMCoreUIUtility.hardcodedString(withKey: "textfield_disabled_state") ?? "")" } - func setFormText(_ formText: String?) { - formLabel?.text = formText - setAccessibilityString(formText) - } - - func setPlaceholder(_ placeholder: String?) { - setPlaceholder(placeholder, with: customPlaceHolderColor ?? UIColor.black) - } - - func setText(_ text: String?) { - textField?.text = text - valueChanged() - } - - func setMfTextFieldDelegate(_ mfTextFieldDelegate: MFTextFieldDelegate?) { - self.mfTextFieldDelegate = mfTextFieldDelegate - 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) - } - } - - func setUiTextFieldDelegate(_ uiTextFieldDelegate: UITextFieldDelegate?) { - uiTextFieldDelegate = uiTextFieldDelegate - textField?.delegate = uiTextFieldDelegate - } - - func setBothTextFieldDelegates(_ delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { + func setBothTextFieldDelegates(_ delegate: (UITextFieldDelegate & TextFieldDelegate)?) { mfTextFieldDelegate = delegate uiTextFieldDelegate = delegate } @@ -425,98 +527,90 @@ import UIKit guard let map = map, !map.isEmpty else { return } - var string = map.string(KeyLabel) - - if (string?.count ?? 0) > 0 { - formText = string + if let formText = map[KeyLabel] as? String { + self.formText = formText } - string = map?.string(KeyValue) - - if (string?.count ?? 0) > 0 { - text = string + if let text = map[KeyValue] as? String { + self.text = text } - string = map?.string(forKey: KeyDisable) - - if string?.isEqual(StringY) ?? false || map?.bool(forKey: KeyDisable) != nil { + if let text = map[KeyDisable] as? String, text.isEqual(StringY) || map.boolForKey(KeyDisable) { enable(false) } - string = map?.string(KeyErrorMessage) - - if (string?.count ?? 0) > 0 { - errMessage = string + if let errMessage = map[KeyErrorMessage] as? String { + self.errMessage = errMessage } // key used to send text value to server - string = map?.string(KeyFieldKey) - - if (string?.count ?? 0) > 0 { - fieldKey = string + if let fieldKey = map[KeyFieldKey] as? String { + self.fieldKey = fieldKey } - string = map?.string(KeyType) - - if (string == "dropDown") { + switch map.stringForkey(KeyType) { + case "dropDown": dropDownCarrotLabel?.isHidden = false self.hasDropDown = true - } else if (string == "password") { + case "password": textField?.isSecureTextEntry = true - } else if (string == "number") { + case "number": textField?.keyboardType = .numberPad - } else if (string == "email") { + case "email": textField?.keyboardType = .emailAddress + + default: + break } - string = map?.string("regex") + let regex = map.stringForkey("regex") - if (string?.count ?? 0) != 0 { + if regex.count != 0 { validationBlock = { enteredValue in - return MVMCoreUIUtility.validate(enteredValue, withRegularExpression: string) + guard let value = enteredValue else { return false } + return MVMCoreUIUtility.validate(value, withRegularExpression: regex) } } else { - setDefaultValidationBlock() + defaultValidationBlock() } } - func setWithMap(_ map: [AnyHashable : Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { - MVMCoreUICommonViewsUtility.addDismissToolbar(textField!, delegate: delegate) + func setWithMap(_ map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & TextFieldDelegate)?) { + + guard let textField = textField else { return } + + MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: delegate) + mfTextFieldDelegate = delegate uiTextFieldDelegate = delegate - textFieldDelegate = delegate setWithMap(map) } - func setValidationBlock(_ validationBlock: @escaping (String?) -> Bool) { - self.validationBlock = validationBlock - valueChanged() - } - - func setDefaultValidationBlock() { - self.validationBlock = { enteredValue in + 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 } - //-------------------------------------------------- - // MARK: - XIB Helpers - //-------------------------------------------------- func showDropDown(_ show: Bool) { + if hasDropDown { dropDownCarrotLabel?.isHidden = !show dropDownCarrotWidth?.isActive = !show @@ -526,99 +620,93 @@ import UIKit } func enable(_ enable: Bool) { - enabled = enable //Set outside the dispatch so that registerAnimations can know about it + + // Set outside the dispatch so that registerAnimations can know about it + enabled = enable + DispatchQueue.main.async { [weak self] in + self?.isUserInteractionEnabled = enable self?.textField?.isUserInteractionEnabled = enable self?.textField?.isEnabled = enable + if enable { self?.textField?.textColor = self?.customEnabledTextColor ?? .black self?.formLabel?.textColor = UIColor.mfBattleshipGrey() - self?.label?.textColor = UIColor.black - if self!.errorShowing { - self?.separatorView?.backgroundColor = UIColor.mfPumpkin() - } else { - self?.separatorView?.backgroundColor = .black - } + self?.placeholderErrorLabel?.textColor = .black + self?.separatorView?.backgroundColor = (self?.errorShowing ?? false) ? UIColor.mfPumpkin() : .black self?.showDropDown(true) + } else { - self?.textField?.textColor = self!.customDisabledTextColor ?? UIColor.mfSilver() + self?.textField?.textColor = self?.customDisabledTextColor ?? UIColor.mfSilver() self?.formLabel?.textColor = UIColor.mfSilver() - self?.label?.textColor = UIColor.mfSilver() + self?.placeholderErrorLabel?.textColor = UIColor.mfSilver() self?.showDropDown(false) - self?.hideError() //should not have error if the field is disabled + self?.hideError() // Should not have error if the field is disabled self?.separatorView?.backgroundColor = UIColor.mfSilver() } } } func showLabel(_ show: Bool) { - label?.isHidden = !show + + placeholderErrorLabel?.isHidden = !show } func dashSeperatorView(_ dash: Bool) { - if dash { - dashLine?.isHidden = false - //never hide seperator view because it could be possiblely used by other classes for positioning - separatorView?.backgroundColor = .clear - } else { - dashLine?.isHidden = true - separatorView?.backgroundColor = .black - } + + // 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 validateBlock() { - valueChanged() - } - func valueChanged() { - // update label for placeholder + // Update label for placeholder if !errorShowing { - label?.text = "" + placeholderErrorLabel?.text = "" } - // Check validity. - let previousValidity = valid - if (validationBlock != nil) { - valid = validationBlock(text) - } else { - //if validation not set, input will always be valid - valid = true - } + let previousValidity = isValid - if previousValidity && !valid { - if (errMessage != nil) { - self.errorMessage = errMessage + // If validation not set, input will always be valid + isValid = validationBlock?(text) ?? true + + if previousValidity && !isValid { + if let errMessage = errMessage { + setErrorMessage(errMessage) } - if mfTextFieldDelegate.responds(to: #selector(entryIsInvalid(_:))) { - mfTextFieldDelegate?.entryIsInvalid!(self) + + if let mfTextFieldDelegate = mfTextFieldDelegate { + mfTextFieldDelegate.entryIsInvalid?(self) } - } else if !previousValidity && valid { + } else if !previousValidity && isValid { hideError() - if mfTextFieldDelegate?.responds(to: #selector(entryIsValid(_:))) { - mfTextFieldDelegate?.entryIsValid!(self) + + if let mfTextFieldDelegate = mfTextFieldDelegate { + mfTextFieldDelegate.entryIsValid?(self) } } } func endInputing() { + if isValid { hideError() - separatorView!.backgroundColor = .black - } else { - if (errMessage != nil) { - self.errorMessage = errMessage - } + separatorView?.backgroundColor = .black + } else if let errMessage = errMessage { + setErrorMessage(errMessage) } } func startEditing() { + textField?.becomeFirstResponder() + if !errorShowing { separatorView?.backgroundColor = .black separatorHeightConstraint?.constant = 1 @@ -626,12 +714,15 @@ import UIKit } class func getEnabledTextfields(_ textFieldToDetermine: [MFTextField]?) -> [AnyHashable]? { - var enabledTextFields: [AnyHashable] = [] + + var enabledTextFields = [AnyHashable]() + for textfield in textFieldToDetermine ?? [] { if textfield.isEnabled { enabledTextFields.append(textfield) } } + return enabledTextFields } } @@ -640,15 +731,21 @@ import UIKit 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{ + if let delegateObject = delegateObject { FormValidator.setupValidation(molecule: self, delegate: delegateObject.formValidationProtocol) - let formValidator = FormValidator.getForDelegate(delegateObject.formValidationProtocol) + setWithMap(json) + + if let formValidationProtocol = delegateObject.formValidationProtocol { + mfTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol) as? TextFieldDelegate + } - self.withMap = json - mfTextFieldDelegate = formValidator uiTextFieldDelegate = delegateObject.uiTextFieldDelegate - MVMCoreUICommonViewsUtility.addDismissToolbar(textField!, delegate: uiTextFieldDelegate) + + if let textField = textField { + MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) + } } } @@ -660,16 +757,10 @@ extension TextField { // MARK: - Accessibility extension TextField { - func formatter() -> DateFormatter? { - if !formatter { - formatter = DateFormatter() - formatter.dateStyle = .medium - formatter.timeZone = .system - formatter.locale = .current - formatter.formatterBehavior = .default - } - return formatter - } +// open override class func accessibilityElements() -> [Any]? { +// return [self.textField] +// } + } // MARK: - Form Validation diff --git a/MVMCoreUI/Atoms/Views/DashLine.swift b/MVMCoreUI/Atoms/Views/DashLine.swift index 512b48cd..926fb791 100644 --- a/MVMCoreUI/Atoms/Views/DashLine.swift +++ b/MVMCoreUI/Atoms/Views/DashLine.swift @@ -17,14 +17,31 @@ open class DashLine: MFView { @objc public var dashColor: UIColor? + //------------------------------------------------------ + // MARK: - Initializer + //------------------------------------------------------ + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public init() { + super.init(frame: .zero) + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + fatalError("init(coder:) has not been implemented") + } + //------------------------------------------------------ // MARK: - Lifecycle //------------------------------------------------------ @objc override open func updateView(_ size: CGFloat) { - MVMCoreDispatchUtility.performBlock(onMainThread: { [weak self] in + DispatchQueue.main.async { [weak self] in self?.setNeedsDisplay() - }) + } } @objc override open func draw(_ rect: CGRect) { diff --git a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m index 64dbe2e6..ffafdc82 100644 --- a/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m +++ b/MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m @@ -36,7 +36,7 @@ @"footer": StandardFooterView.class, @"caretView": CaretView.class, @"caretButton": CaretButton.class, - @"textField" : MFTextField.class, + @"textField" : TextField.class, @"digitTextField" : MFDigitTextField.class, @"checkbox" : Checkbox.class, @"checkboxWithLabelView" : CheckboxWithLabelView.class,