From d4df5745f439a5f4b75ac1e8a9d685d4053a55b5 Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Fri, 11 Oct 2019 13:23:49 -0400 Subject: [PATCH] Further swiftification. --- MVMCoreUI/Atoms/TextFields/TextField.swift | 412 ++++++++++----------- 1 file changed, 198 insertions(+), 214 deletions(-) diff --git a/MVMCoreUI/Atoms/TextFields/TextField.swift b/MVMCoreUI/Atoms/TextFields/TextField.swift index 4df53197..7fa50d44 100644 --- a/MVMCoreUI/Atoms/TextFields/TextField.swift +++ b/MVMCoreUI/Atoms/TextFields/TextField.swift @@ -18,7 +18,7 @@ import UIKit @objc optional func dismissFieldInput(_ sender: Any?) } -@objcMembers open class TextField: ViewConstrainingView, MVMCoreUIMoleculeViewProtocol, FormValidationProtocol { +@objcMembers open class TextField: ViewConstrainingView { //-------------------------------------------------- // MARK: - Outlets //-------------------------------------------------- @@ -30,41 +30,57 @@ import UIKit var separatorView: UIView? 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 + } + } + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - weak var text: String? - weak var formText: String? - weak var fieldKey: String? - weak var placeholder: String? + weak var label: UILabel? - // If you're using a MFViewController, you must set this to it - weak var uiTextFieldDelegate: UITextFieldDelegate? - // 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. - weak var mfTextFieldDelegate: MFTextFieldDelegate? + //accesories + weak var pickerView: UIPickerView? + + //utility + var observingForChanges = false + var errorShowing = false + var hasDropDown = false - /*make it public so outsider class can know the posistion of it. */ + var dashLine: DashLine? + var dropDownCarrotLabel: UILabel? + var dropDownCarrotWidth: NSLayoutConstraint? + + var text: String? + var formText: String? + var fieldKey: String? + var placeholder: String? var enabled = false - // To set the placeholder and text - - /* will move out in Feb release */ + var hideBorder = false weak var datePicker: UIDatePicker? private(set) weak var toolbar: UIToolbar? - //helper var formatter: DateFormatter? var valid = false var validationBlock: ((_ enteredValue: String?) -> Bool)? - // custom text colors + var customEnabledTextColor: UIColor? var customDisabledTextColor: UIColor? - //default error message var errMessage: String? var editCompleteAction: ((_ text: String?) -> ())? @@ -91,72 +107,47 @@ import UIKit public override init(frame: CGRect) { super.init(frame: frame) + + translatesAutoresizingMaskIntoConstraints = false + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + fatalError("init(coder:) has not been implemented") } 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() { - self.init(frame: .zero) - dropDownCarrotLabel().hidden = false - hasDropDown = false - } - - public convenience init(withBothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { - self.init(frame: .zero) - bothTextFieldDelegates = delegate - } - - public convenience init(withMap map: [AnyHashable : Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { - self.init(frame: .zero) - translatesAutoresizingMaskIntoConstraints = false - setWithMap(map, bothDelegates: delegate) - } - - public convenience init(withBothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { - self.init(frame: .zero) - bothTextFieldDelegates = delegate - } - - */ - - class func mfTextField() -> Self? { + public convenience init(withMap map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { + self.init(frame: .zero) - let view = self.init() - view?.hasDropDown = false - return view + setWithMap(map, bothDelegates: delegate) + } + + public convenience init(mfTextFieldForDropDown: Bool) { + self.init(frame: .zero) + + dropDownCarrotLabel?.isHidden = false + hasDropDown = true } - class func mfTextField(withBothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) -> Self? { + public convenience init(mfTextFieldForDropDownWithMap map: [AnyHashable: Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { + self.init(frame: .zero) - let textField = self.mfTextField() - textField?.bothTextFieldDelegates = delegate - return textField - } - - class func mfTextField(withMap map: [AnyHashable : Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) -> Self? { - - let textField = self.mfTextField() - textField?.translatesAutoresizingMaskIntoConstraints = false - textField?.setWithMap(map, bothDelegates: delegate) - return textField - } - - class func mfTextFieldForDropDown() -> Self? { - - let textField = self.mfTextField() - textField?.dropDownCarrotLabel().hidden = false - textField?.hasDropDown = true - return textField - } - - class func mfTextFieldForDropDown(withBothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) -> Self? { - - let textField = self.mfTextFieldForDropDown() - textField?.bothTextFieldDelegates = delegate - return textField + dropDownCarrotLabel?.isHidden = false + hasDropDown = true + setWithMap(map, bothDelegates: delegate) } //-------------------------------------------------- @@ -169,59 +160,59 @@ import UIKit translatesAutoresizingMaskIntoConstraints = false - - backgroundColor = .clear + 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.hidden = true - separatorView.backgroundColor = UIColor.black - dashLine.backgroundColor = UIColor.white - dashLine.hidden = 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 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 + } } - func updateView(_ size: CGFloat) { - + open override func updateView(_ size: CGFloat) { super.updateView(size) + DispatchQueue.main.async { [weak self] in + self?.formLabel?.updateView(size) - self?.label.font = MFStyler.fontForTextFieldUnderLabel() - let textField = self?.textField { - MFStyler.styleTextField(textField) - } - self?.dashLine.updateView(size) + self?.label?.font = MFStyler.fontForTextFieldUnderLabel() +// let textFieldView = { + MFStyler.styleTextField(self!.textField!) +// } + self?.dashLine?.updateView(size) } } @@ -230,28 +221,23 @@ import UIKit uiTextFieldDelegate = nil } - func draw(_ rect: CGRect) { + open override func draw(_ rect: CGRect) { super.draw(rect) - borderPath.removeAllPoints() + + 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)) + 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 - var strokeColor: UIColor? - if errorShowing { - strokeColor = UIColor.mfPumpkin() - } else { - strokeColor = UIColor.mfSilver() - } - - strokeColor?.setStroke() - + let strokeColor = errorShowing ? UIColor.mfPumpkin() : UIColor.mfSilver() + strokeColor.setStroke() borderPath?.stroke() } } @@ -260,6 +246,14 @@ import UIKit // MARK: - Methods //-------------------------------------------------- + func placeholder() -> String? { + return textField.attributedPlaceholder.string + } + + func text() -> String? { + return textField.text() + } + // MARK: - Utilities func createDatePicker() { //tool bar @@ -302,12 +296,12 @@ import UIKit } func dismissDatePicker() -> Date? { - let pickedDate = datePicker!.date - if let pickedDate = pickedDate { - if calendar.isDate(pickedDate, inSameDayAs: Date()) { + var pickedDate = datePicker!.date + if let pickedDatei = pickedDate { + if calendar.isDate(pickedDatei, inSameDayAs: Date()) { text = MVMCoreUIUtility.hardcodedString(withKey: "textfield_today_string") } else { - self.text = formatter.string(from: pickedDate) + self.text = formatter.string(from: pickedDatei) } } textField?.resignFirstResponder() @@ -325,9 +319,9 @@ import UIKit 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?.label?.text = errorMessage + self?.label?.numberOfLines = 0 + self?.textField?.accessibilityValue() = String(format: MVMCoreUIUtility.hardcodedString(withKey: "textfield_error_message"), self.textField.text() ?? "", errorMessage ?? "") self?.setNeedsDisplay() self?.layoutIfNeeded() } @@ -336,7 +330,7 @@ import UIKit func pushAccessibilityNotification() { DispatchQueue.main.async { [weak self] in - UIAccessibilityPostNotification(UIAccessibility.Notification.layoutChanged, self?.textField) + UIAccessibility.post(notification: UIAccessibility.Notification.layoutChanged, argument: self?.textField) } } @@ -347,22 +341,14 @@ import UIKit self?.separatorView?.backgroundColor = UIColor.black self?.layoutIfNeeded() self?.errorShowing = false - self?.label.textColor = UIColor.black - self?.label.text() = "" + self?.label?.textColor = UIColor.black + self?.label?.text = "" self?.textField?.accessibilityValue = nil self?.setNeedsDisplay() self?.layoutIfNeeded() } } - func placeholder() -> String? { - return textField.attributedPlaceholder.string - } - - func text() -> String? { - return textField.text() - } - // MARK: - Setters func setPlaceholder(_ placeholder: String?, with color: UIColor?) { @@ -375,10 +361,10 @@ import UIKit ]) } } - if textField.text.length > 0 && !errorShowing { - label.text = placeholder + if textField?.text.length > 0 && !errorShowing { + label?.text = placeholder } else if !errorShowing { - label.text = "" + label?.text = "" } setAccessibilityString(placeholder) } @@ -430,7 +416,7 @@ import UIKit } func setUiTextFieldDelegate(_ uiTextFieldDelegate: UITextFieldDelegate?) { - self.uiTextFieldDelegate = uiTextFieldDelegate + uiTextFieldDelegate = uiTextFieldDelegate textField?.delegate = uiTextFieldDelegate } @@ -462,19 +448,22 @@ import UIKit } string = map?.string(KeyErrorMessage) + if (string?.count ?? 0) > 0 { errMessage = string } // key used to send text value to server string = map?.string(KeyFieldKey) + if (string?.count ?? 0) > 0 { fieldKey = string } string = map?.string(KeyType) + if (string == "dropDown") { - dropDownCarrotLabel().hidden = false + dropDownCarrotLabel?.isHidden = false self.hasDropDown = true } else if (string == "password") { @@ -488,6 +477,7 @@ import UIKit } string = map?.string("regex") + if (string?.count ?? 0) != 0 { validationBlock = { enteredValue in return MVMCoreUIUtility.validate(enteredValue, withRegularExpression: string) @@ -499,7 +489,8 @@ import UIKit func setWithMap(_ map: [AnyHashable : Any]?, bothDelegates delegate: (UITextFieldDelegate & MFTextFieldDelegate)?) { MVMCoreUICommonViewsUtility.addDismissToolbar(textField!, delegate: delegate) - self.bothTextFieldDelegates = delegate + uiTextFieldDelegate = delegate + textFieldDelegate = delegate setWithMap(map) } @@ -510,35 +501,29 @@ import UIKit func setDefaultValidationBlock() { self.validationBlock = { enteredValue in - if (enteredValue?.count ?? 0) > 0 { - return true - } else { - return false - } + return (enteredValue?.count ?? 0) > 0 } } - override func setLeftPinConstant(_ constant: CGFloat) { + open override func setLeftPinConstant(_ constant: CGFloat) { textContainerLeftPin?.constant = constant errorLableLeftPin?.constant = constant formLabelLeftPin?.constant = constant } - func setRightPinConstant(_ constant: CGFloat) { + 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.hidden = !show - dropDownCarrotWidth.active = !show + dropDownCarrotLabel?.isHidden = !show + dropDownCarrotWidth?.isActive = !show setNeedsLayout() layoutIfNeeded() } @@ -548,41 +533,41 @@ import UIKit enabled = enable //Set outside the dispatch so that registerAnimations can know about it DispatchQueue.main.async { [weak self] in self?.isUserInteractionEnabled = enable - self?.textField.userInteractionEnabled = enable - self?.textField.isEnabled = 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 + self?.textField?.textColor = self?.customEnabledTextColor ?? .black + self?.formLabel?.textColor = UIColor.mfBattleshipGrey() + self?.label?.textColor = UIColor.black if self!.errorShowing { - self?.separatorView.backgroundColor = UIColor.mfPumpkin() + self?.separatorView?.backgroundColor = UIColor.mfPumpkin() } else { - self?.separatorView.backgroundColor = .black + self?.separatorView?.backgroundColor = .black } self?.showDropDown(true) } else { - self?.textField.textColor = self!.customDisabledTextColor ?? UIColor.mfSilver() - self?.formLabel.textColor = UIColor.mfSilver() - self?.label.textColor = UIColor.mfSilver() + self?.textField?.textColor = self!.customDisabledTextColor ?? UIColor.mfSilver() + self?.formLabel?.textColor = UIColor.mfSilver() + self?.label?.textColor = UIColor.mfSilver() self?.showDropDown(false) self?.hideError() //should not have error if the field is disabled - self?.separatorView.backgroundColor = UIColor.mfSilver() + self?.separatorView?.backgroundColor = UIColor.mfSilver() } } } func showLabel(_ show: Bool) { - label?.hidden = !show + label?.isHidden = !show } func dashSeperatorView(_ dash: Bool) { if dash { - dashLine?.hidden = false + dashLine?.isHidden = false //never hide seperator view because it could be possiblely used by other classes for positioning separatorView?.backgroundColor = .clear } else { - dashLine?.hidden = true - separatorView.backgroundColor = .black + dashLine?.isHidden = true + separatorView?.backgroundColor = .black } } @@ -603,7 +588,7 @@ import UIKit // Check validity. let previousValidity = valid - if validationBlock { + if (validationBlock != nil) { valid = validationBlock(text) } else { //if validation not set, input will always be valid @@ -611,7 +596,7 @@ import UIKit } if previousValidity && !valid { - if errMessage { + if (errMessage != nil) { self.errorMessage = errMessage } if mfTextFieldDelegate.responds(to: #selector(entryIsInvalid(_:))) { @@ -653,29 +638,13 @@ import UIKit } return enabledTextFields } +} + +// MARK: - Molecular +extension TextField { - //-------------------------------------------------- - // MARK: - Accessibility - //-------------------------------------------------- - - func formatter() -> DateFormatter? { - if !formatter { - formatter = DateFormatter() - formatter.dateStyle = .medium - formatter.timeZone = NSTimeZone.system - formatter.locale = NSLocale.current - formatter.formatterBehavior = .default - } - return formatter - } - - - - //-------------------------------------------------- - // MARK: - Molecular - //-------------------------------------------------- - - override open func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) { + override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) { + if (delegateObject is MVMCoreUIDelegateObject) { FormValidator.setupValidation(withMolecule: self, delegate: delegateObject?.formValidationProtocol) let formValidator = FormValidator.getForDelegate(delegateObject?.formValidationProtocol) @@ -683,17 +652,32 @@ import UIKit self.withMap = json mfTextFieldDelegate = formValidator uiTextFieldDelegate = delegateObject?.uiTextFieldDelegate - MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: uiTextFieldDelegate) + MVMCoreUICommonViewsUtility.addDismissToolbar(textField!, delegate: uiTextFieldDelegate) } } override open class func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat { return 76 } +} + +// MARK: - Accessibility +extension TextField { - //-------------------------------------------------- - // MARK: - Form Validation - //-------------------------------------------------- + func formatter() -> DateFormatter? { + if !formatter { + formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeZone = .system + formatter.locale = .current + formatter.formatterBehavior = .default + } + return formatter + } +} + +// MARK: - Form Validation +extension TextField: FormValidationProtocol { public func isValidField() -> Bool { return isValid