saving state.
This commit is contained in:
parent
64f4356a74
commit
74abef0c30
@ -36,6 +36,7 @@
|
|||||||
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */; };
|
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */; };
|
||||||
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
|
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
|
||||||
0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; };
|
0A41BA7F23453A6400D4C0BC /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */; };
|
||||||
|
0A6BF4722360C56C0028F841 /* DropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */; };
|
||||||
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
|
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
|
||||||
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; };
|
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; };
|
||||||
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
|
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
|
||||||
@ -217,6 +218,7 @@
|
|||||||
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryField.swift; sourceTree = "<group>"; };
|
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitEntryField.swift; sourceTree = "<group>"; };
|
||||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; };
|
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; };
|
||||||
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
|
0A41BA7E23453A6400D4C0BC /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = "<group>"; };
|
||||||
|
0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownEntryField.swift; sourceTree = "<group>"; };
|
||||||
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
|
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
|
||||||
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = "<group>"; };
|
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = "<group>"; };
|
||||||
0A8321A72355062F00CB7F00 /* MdnTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnTextField.swift; sourceTree = "<group>"; };
|
0A8321A72355062F00CB7F00 /* MdnTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MdnTextField.swift; sourceTree = "<group>"; };
|
||||||
@ -770,6 +772,7 @@
|
|||||||
0A21DB80235DF87300C160A2 /* TextField.swift */,
|
0A21DB80235DF87300C160A2 /* TextField.swift */,
|
||||||
0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */,
|
0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */,
|
||||||
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */,
|
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */,
|
||||||
|
0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */,
|
||||||
);
|
);
|
||||||
path = TextFields;
|
path = TextFields;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1125,6 +1128,7 @@
|
|||||||
D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */,
|
D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */,
|
||||||
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
|
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
|
||||||
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
|
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
|
||||||
|
0A6BF4722360C56C0028F841 /* DropdownEntryField.swift in Sources */,
|
||||||
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,
|
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,
|
||||||
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
|
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
|
||||||
D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */,
|
D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */,
|
||||||
|
|||||||
483
MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift
Normal file
483
MVMCoreUI/Atoms/TextFields/DropdownEntryField.swift
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
//
|
||||||
|
// DropdownEntryField.swift
|
||||||
|
// MVMCoreUI
|
||||||
|
//
|
||||||
|
// Created by Kevin Christiano on 10/23/19.
|
||||||
|
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
|
@objcMembers open class DropdownEntryField: TextEntryField {
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Outlets
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
private var calendar: Calendar?
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Properties
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public var dropDownCaretLabel: UILabel?
|
||||||
|
|
||||||
|
public var enabledTextColor: UIColor?
|
||||||
|
public var disabledTextColor: UIColor?
|
||||||
|
|
||||||
|
public var observingForChanges = false
|
||||||
|
|
||||||
|
private var borderPath: UIBezierPath?
|
||||||
|
|
||||||
|
public override var isEnabled: Bool {
|
||||||
|
didSet {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The text of this textField.
|
||||||
|
public override var text: String? {
|
||||||
|
get { return textField?.text }
|
||||||
|
set {
|
||||||
|
textField?.text = newValue
|
||||||
|
valueChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var validationBlock: ((_ enteredValue: String?) -> Bool)? {
|
||||||
|
didSet {
|
||||||
|
valueChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Constraints
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public var dropDownCaretWidth: 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()
|
||||||
|
dropDownCaretLabel?.isHidden = hasDropDown
|
||||||
|
self.hasDropDown = !hasDropDown
|
||||||
|
setWithMap(map, bothDelegates: bothDelegates)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
open override func setupFieldContainerContent(_ container: UIView) {
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
container.addSubview(textField)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
textField.topAnchor.constraint(equalTo: container.topAnchor, constant: 10),
|
||||||
|
textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
|
||||||
|
container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 10)])
|
||||||
|
|
||||||
|
let dropDownCaretLabel = Label()
|
||||||
|
self.dropDownCaretLabel = dropDownCaretLabel
|
||||||
|
dropDownCaretLabel.setContentHuggingPriority(UILayoutPriority(900), for: .horizontal)
|
||||||
|
dropDownCaretLabel.setContentHuggingPriority(UILayoutPriority(251), for: .vertical)
|
||||||
|
dropDownCaretLabel.setContentCompressionResistancePriority(UILayoutPriority(900), for: .horizontal)
|
||||||
|
dropDownCaretLabel.isHidden = true
|
||||||
|
dropDownCaretLabel.isUserInteractionEnabled = true
|
||||||
|
let tapOnCarrot = UITapGestureRecognizer(target: self, action: #selector(startEditing))
|
||||||
|
dropDownCaretLabel.addGestureRecognizer(tapOnCarrot)
|
||||||
|
|
||||||
|
container.addSubview(dropDownCaretLabel)
|
||||||
|
|
||||||
|
dropDownCaretLabel.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
|
||||||
|
dropDownCaretLabel.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 6).isActive = true
|
||||||
|
container.trailingAnchor.constraint(equalTo: dropDownCaretLabel.trailingAnchor, constant: 16).isActive = true
|
||||||
|
container.bottomAnchor.constraint(equalTo: dropDownCaretLabel.bottomAnchor).isActive = true
|
||||||
|
dropDownCarrotWidth = dropDownCaretLabel.widthAnchor.constraint(equalToConstant: 0)
|
||||||
|
dropDownCarrotWidth?.isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func updateView(_ size: CGFloat) {
|
||||||
|
super.updateView(size)
|
||||||
|
|
||||||
|
if let textField = textField {
|
||||||
|
MFStyler.styleTextField(textField)
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
mfTextFieldDelegate = nil
|
||||||
|
uiTextFieldDelegate = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func draw(_ rect: CGRect) {
|
||||||
|
super.draw(rect)
|
||||||
|
|
||||||
|
borderPath?.removeAllPoints()
|
||||||
|
|
||||||
|
if !hideBorder, let frame = fieldContainer?.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
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
textField?.accessibilityValue = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setBothTextFieldDelegates(_ delegate: (UITextFieldDelegate & TextFieldDelegate)?) {
|
||||||
|
|
||||||
|
mfTextFieldDelegate = delegate
|
||||||
|
uiTextFieldDelegate = delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
public override func setWithMap(_ map: [AnyHashable: Any]?) {
|
||||||
|
super.setWithMap(map)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
formIsDisabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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 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 = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [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
|
||||||
|
extension TextEntryField {
|
||||||
|
|
||||||
|
open override func pushAccessibilityNotification() {
|
||||||
|
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
UIAccessibility.post(notification: .layoutChanged, argument: self.textField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override 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") ?? "")"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is intended to be subclassed by a class that will add views subclassed under UIControl.
|
* This class is intended to be subclassed by a class that will add views subclassed under UIControl.
|
||||||
* The FieldEntryForm provides the base logic for the description label, placeholder/error label and field container.
|
* The FieldEntryForm provides the base logic for the description label, placeholder/error label and field container.
|
||||||
@ -18,13 +17,13 @@ import UIKit
|
|||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
private(set) var descriptionLabel: Label?
|
||||||
|
private(set) var feedbackLabel: Label?
|
||||||
|
private(set) var fieldContainer: UIView?
|
||||||
|
|
||||||
public var backgroundView: UIView?
|
public var backgroundView: UIView?
|
||||||
public var formDescriptionLabel: Label?
|
|
||||||
public var fieldContainer: UIView?
|
|
||||||
public var placeholderErrorLabel: Label?
|
|
||||||
public var separatorView: UIView?
|
public var separatorView: UIView?
|
||||||
public var dashLine: DashLine?
|
public var dashLine: DashLine?
|
||||||
public var dropDownCaretLabel: UILabel?
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Accessories
|
// MARK: - Accessories
|
||||||
@ -38,43 +37,67 @@ import UIKit
|
|||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var showError = false
|
public var isValid = false
|
||||||
public var hasDropDown = false
|
public var fieldKey: String?
|
||||||
|
|
||||||
private var borderPath: UIBezierPath?
|
private var borderPath: UIBezierPath?
|
||||||
public var isEnabled = true
|
|
||||||
|
public var errorMessage: String?
|
||||||
|
public var showErrorMessage = false
|
||||||
|
|
||||||
|
public var isEnabled = true {
|
||||||
|
didSet {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
self.isUserInteractionEnabled = self.isEnabled
|
||||||
|
self.descriptionLabel?.textColor = self.isEnabled ? UIColor.mfBattleshipGrey() : UIColor.mfSilver()
|
||||||
|
self.feedbackLabel?.textColor = self.isEnabled ? .black : UIColor.mfSilver()
|
||||||
|
self.separatorView?.backgroundColor = self.showErrorMessage ? UIColor.mfPumpkin() : .black
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines if a border should be drawn.
|
/// Determines if a border should be drawn.
|
||||||
public var hideBorder = false {
|
public var hideBorder = false {
|
||||||
didSet { setNeedsLayout() }
|
didSet { setNeedsDisplay() }
|
||||||
}
|
}
|
||||||
|
|
||||||
public var formText: String? {
|
public var descriptionText: String? {
|
||||||
get { return formDescriptionLabel?.text }
|
get { return descriptionLabel?.text }
|
||||||
set {
|
set {
|
||||||
formDescriptionLabel?.text = newValue
|
descriptionLabel?.text = newValue
|
||||||
setAccessibilityString(newValue)
|
setAccessibilityString(newValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override this with logic of the textfield(s) that are of focus in this form.
|
/// Override this to conveniently get/set the textfield(s).
|
||||||
public var text: String? {
|
public var text: String? {
|
||||||
get { return "" }
|
get { return nil }
|
||||||
set { }
|
set {
|
||||||
|
fatalError("You're supposed to override FormEntryField's 'text' variable.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var placeholderTextColor: UIColor = .black
|
/// Sets feedback text in the textField.
|
||||||
|
public var feedback: String? {
|
||||||
/// Setgs placeholder text in the textField.
|
get { return feedbackLabel?.text }
|
||||||
public var placeholder: String? {
|
|
||||||
get { return placeholderErrorLabel?.text }
|
|
||||||
set {
|
set {
|
||||||
guard let newPlaceholderText = newValue else { return }
|
guard isEnabled,
|
||||||
|
let newFeedback = newValue
|
||||||
|
else { return }
|
||||||
|
|
||||||
if !showError {
|
DispatchQueue.main.async { [weak self] in
|
||||||
placeholderErrorLabel?.text = newPlaceholderText
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
self.feedbackLabel?.text = newFeedback
|
||||||
|
self.separatorHeightConstraint?.constant = self.showErrorMessage ? 4 : 1
|
||||||
|
self.separatorView?.backgroundColor = self.showErrorMessage ? UIColor.mfPumpkin() : .black
|
||||||
|
self.setNeedsDisplay()
|
||||||
|
self.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
setAccessibilityString(newPlaceholderText)
|
setAccessibilityString(newFeedback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,30 +112,18 @@ import UIKit
|
|||||||
return formatter
|
return formatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
public var isValid = false
|
|
||||||
public var fieldKey: String?
|
|
||||||
|
|
||||||
public var enabledTextColor: UIColor?
|
|
||||||
public var disabledTextColor: UIColor?
|
|
||||||
|
|
||||||
public var errorMessage: String?
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Constraints
|
// MARK: - Constraints
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var heightConstraint: NSLayoutConstraint?
|
public var textContainerLeading: NSLayoutConstraint?
|
||||||
|
public var textContainerTrailing: NSLayoutConstraint?
|
||||||
|
|
||||||
public var dropDownCarrotWidth: NSLayoutConstraint?
|
public var errorLabelTrailing: NSLayoutConstraint?
|
||||||
|
public var errorLabelLeading: NSLayoutConstraint?
|
||||||
|
|
||||||
public var textContainerLeftPin: NSLayoutConstraint?
|
public var descriptionLabelLeading: NSLayoutConstraint?
|
||||||
public var textContainerRightPin: NSLayoutConstraint?
|
public var descriptionLabelTrailing: NSLayoutConstraint?
|
||||||
|
|
||||||
public var errorLableRightPin: NSLayoutConstraint?
|
|
||||||
public var errorLableLeftPin: NSLayoutConstraint?
|
|
||||||
|
|
||||||
public var formDescriptionLabelLeftPin: NSLayoutConstraint?
|
|
||||||
public var formDescriptionLabelRightPin: NSLayoutConstraint?
|
|
||||||
|
|
||||||
public var separatorHeightConstraint: NSLayoutConstraint?
|
public var separatorHeightConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
@ -123,8 +134,8 @@ import UIKit
|
|||||||
/// This must be overriden by a subclass.
|
/// This must be overriden by a subclass.
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
setupView()
|
setupView()
|
||||||
self.hasDropDown = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This must be overriden by a subclass.
|
/// This must be overriden by a subclass.
|
||||||
@ -134,7 +145,7 @@ import UIKit
|
|||||||
|
|
||||||
required public init?(coder: NSCoder) {
|
required public init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
fatalError("TextEntryField does not support xib.")
|
fatalError("FormEntryField does not support xib.")
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -151,21 +162,21 @@ import UIKit
|
|||||||
setContentCompressionResistancePriority(.required, for: .vertical)
|
setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
backgroundColor = .clear
|
backgroundColor = .clear
|
||||||
|
|
||||||
let formDescriptionLabel = Label()
|
let descriptionLabel = Label()
|
||||||
self.formDescriptionLabel = formDescriptionLabel
|
self.descriptionLabel = descriptionLabel
|
||||||
formDescriptionLabel.font = MFStyler.fontB3()
|
descriptionLabel.font = MFStyler.fontB3()
|
||||||
formDescriptionLabel.textColor = UIColor.mfBattleshipGrey()
|
descriptionLabel.textColor = UIColor.mfBattleshipGrey()
|
||||||
formDescriptionLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal)
|
descriptionLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal)
|
||||||
formDescriptionLabel.setContentHuggingPriority(UILayoutPriority(251), for: .vertical)
|
descriptionLabel.setContentHuggingPriority(UILayoutPriority(251), for: .vertical)
|
||||||
formDescriptionLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
descriptionLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
|
||||||
addSubview(formDescriptionLabel)
|
addSubview(descriptionLabel)
|
||||||
|
|
||||||
formDescriptionLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
|
descriptionLabel.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||||
formDescriptionLabelLeftPin = formDescriptionLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
descriptionLabelLeading = descriptionLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
||||||
formDescriptionLabelLeftPin?.isActive = true
|
descriptionLabelLeading?.isActive = true
|
||||||
formDescriptionLabelRightPin = layoutMarginsGuide.trailingAnchor.constraint(equalTo: formDescriptionLabel.trailingAnchor)
|
descriptionLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: descriptionLabel.trailingAnchor)
|
||||||
formDescriptionLabelRightPin?.isActive = true
|
descriptionLabelLeading?.isActive = true
|
||||||
|
|
||||||
let fieldContainer = UIView(frame: .zero)
|
let fieldContainer = UIView(frame: .zero)
|
||||||
self.fieldContainer = fieldContainer
|
self.fieldContainer = fieldContainer
|
||||||
@ -174,39 +185,41 @@ import UIKit
|
|||||||
addSubview(fieldContainer)
|
addSubview(fieldContainer)
|
||||||
setupFieldContainer(fieldContainer)
|
setupFieldContainer(fieldContainer)
|
||||||
|
|
||||||
fieldContainer.topAnchor.constraint(equalTo: formDescriptionLabel.bottomAnchor, constant: 4).isActive = true
|
fieldContainer.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 4).isActive = true
|
||||||
textContainerLeftPin = fieldContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
textContainerLeading = fieldContainer.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
||||||
textContainerLeftPin?.isActive = true
|
textContainerLeading?.isActive = true
|
||||||
textContainerRightPin = layoutMarginsGuide.trailingAnchor.constraint(equalTo: fieldContainer.trailingAnchor)
|
textContainerTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: fieldContainer.trailingAnchor)
|
||||||
textContainerRightPin?.isActive = true
|
textContainerTrailing?.isActive = true
|
||||||
|
|
||||||
let placeholderErrorLabel = Label()
|
let feedbackLabel = Label()
|
||||||
self.placeholderErrorLabel = placeholderErrorLabel
|
self.feedbackLabel = feedbackLabel
|
||||||
placeholderErrorLabel.font = MFStyler.fontForTextFieldUnderLabel()
|
feedbackLabel.font = MFStyler.fontForTextFieldUnderLabel()
|
||||||
placeholderErrorLabel.textColor = .black
|
feedbackLabel.textColor = .black
|
||||||
placeholderErrorLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal)
|
feedbackLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal)
|
||||||
placeholderErrorLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal)
|
feedbackLabel.setContentHuggingPriority(UILayoutPriority(251), for: .horizontal)
|
||||||
placeholderErrorLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
feedbackLabel.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||||
|
|
||||||
addSubview(placeholderErrorLabel)
|
addSubview(feedbackLabel)
|
||||||
|
|
||||||
placeholderErrorLabel.topAnchor.constraint(equalTo: fieldContainer.bottomAnchor).isActive = true
|
feedbackLabel.topAnchor.constraint(equalTo: fieldContainer.bottomAnchor).isActive = true
|
||||||
errorLableLeftPin = placeholderErrorLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
errorLabelLeading = feedbackLabel.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor)
|
||||||
errorLableLeftPin?.isActive = true
|
errorLabelLeading?.isActive = true
|
||||||
errorLableRightPin = layoutMarginsGuide.trailingAnchor.constraint(equalTo: placeholderErrorLabel.trailingAnchor)
|
errorLabelTrailing = layoutMarginsGuide.trailingAnchor.constraint(equalTo: feedbackLabel.trailingAnchor)
|
||||||
errorLableRightPin?.isActive = true
|
errorLabelTrailing?.isActive = true
|
||||||
layoutMarginsGuide.bottomAnchor.constraint(equalTo: placeholderErrorLabel.bottomAnchor).isActive = true
|
layoutMarginsGuide.bottomAnchor.constraint(equalTo: feedbackLabel.bottomAnchor).isActive = true
|
||||||
|
|
||||||
setNeedsLayout()
|
setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Method to override.
|
/**
|
||||||
/// Intended to add the interactive content (textField) to the fieldContainer.
|
Method to override.
|
||||||
|
Intended to add the interactive content (textField) to the fieldContainer.
|
||||||
|
*/
|
||||||
open func setupFieldContainerContent(_ container: UIView) {
|
open func setupFieldContainerContent(_ container: UIView) {
|
||||||
// To Be Overridden
|
// To Be Overridden By Subclass.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration logic for the text container view.
|
/// Configuration for the field container view.
|
||||||
private func setupFieldContainer(_ parentView: UIView) {
|
private func setupFieldContainer(_ parentView: UIView) {
|
||||||
|
|
||||||
let backgroundView = UIView(frame: .zero)
|
let backgroundView = UIView(frame: .zero)
|
||||||
@ -253,13 +266,17 @@ import UIKit
|
|||||||
open override func updateView(_ size: CGFloat) {
|
open override func updateView(_ size: CGFloat) {
|
||||||
super.updateView(size)
|
super.updateView(size)
|
||||||
|
|
||||||
formDescriptionLabel?.updateView(size)
|
descriptionLabel?.updateView(size)
|
||||||
placeholderErrorLabel?.font = MFStyler.fontForTextFieldUnderLabel()
|
feedbackLabel?.font = MFStyler.fontForTextFieldUnderLabel()
|
||||||
dashLine?.updateView(size)
|
dashLine?.updateView(size)
|
||||||
|
|
||||||
layoutIfNeeded()
|
layoutIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------
|
||||||
|
// MARK: - Drawing
|
||||||
|
//--------------------------------------------------
|
||||||
|
|
||||||
open override func draw(_ rect: CGRect) {
|
open override func draw(_ rect: CGRect) {
|
||||||
super.draw(rect)
|
super.draw(rect)
|
||||||
|
|
||||||
@ -274,7 +291,7 @@ import UIKit
|
|||||||
borderPath?.addLine(to: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y + frame.size.height))
|
borderPath?.addLine(to: CGPoint(x: frame.origin.x + frame.size.width, y: frame.origin.y + frame.size.height))
|
||||||
borderPath?.lineWidth = 1
|
borderPath?.lineWidth = 1
|
||||||
|
|
||||||
let strokeColor = showError ? UIColor.mfPumpkin() : UIColor.mfSilver()
|
let strokeColor = showErrorMessage ? UIColor.mfPumpkin() : UIColor.mfSilver()
|
||||||
strokeColor.setStroke()
|
strokeColor.setStroke()
|
||||||
|
|
||||||
borderPath?.stroke()
|
borderPath?.stroke()
|
||||||
@ -282,142 +299,30 @@ import UIKit
|
|||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Methods
|
// MARK: - Constraint Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
open func showErrorDropdown(_ show: Bool) {
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
self.showError = show
|
|
||||||
self.separatorHeightConstraint?.constant = show ? 4 : 1
|
|
||||||
self.separatorView?.backgroundColor = show ? UIColor.mfPumpkin() : .black
|
|
||||||
self.setNeedsDisplay()
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func showErrorMessage(_ errorMessage: String?) {
|
|
||||||
|
|
||||||
guard isEnabled else { return }
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
self.separatorHeightConstraint?.constant = 4
|
|
||||||
self.showError = true
|
|
||||||
self.separatorView?.backgroundColor = UIColor.mfPumpkin()
|
|
||||||
self.placeholderErrorLabel?.text = errorMessage
|
|
||||||
self.placeholderErrorLabel?.numberOfLines = 0
|
|
||||||
self.setNeedsDisplay()
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
self.showErrorDropdown(self.showError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func hideError() {
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
self.separatorHeightConstraint?.constant = 1
|
|
||||||
self.separatorView?.backgroundColor = .black
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
self.showError = false
|
|
||||||
self.placeholderErrorLabel?.textColor = .black
|
|
||||||
self.placeholderErrorLabel?.text = ""
|
|
||||||
self.setNeedsDisplay()
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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[KeyDisable] as? String, text.isEqual(StringY) || map.boolForKey(KeyDisable) {
|
|
||||||
formIsDisabled()
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open override func setLeftPinConstant(_ constant: CGFloat) {
|
open override func setLeftPinConstant(_ constant: CGFloat) {
|
||||||
|
|
||||||
textContainerLeftPin?.constant = constant
|
textContainerLeading?.constant = constant
|
||||||
errorLableLeftPin?.constant = constant
|
errorLabelLeading?.constant = constant
|
||||||
formDescriptionLabelLeftPin?.constant = constant
|
descriptionLabelLeading?.constant = constant
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func setRightPinConstant(_ constant: CGFloat) {
|
open override func setRightPinConstant(_ constant: CGFloat) {
|
||||||
|
|
||||||
textContainerRightPin?.constant = constant
|
textContainerTrailing?.constant = constant
|
||||||
errorLableRightPin?.constant = constant
|
errorLabelTrailing?.constant = constant
|
||||||
formDescriptionLabelRightPin?.constant = constant
|
descriptionLabelTrailing?.constant = constant
|
||||||
}
|
}
|
||||||
|
|
||||||
public func showDropDown(_ show: Bool) {
|
//--------------------------------------------------
|
||||||
|
// MARK: - Methods
|
||||||
if hasDropDown {
|
//--------------------------------------------------
|
||||||
dropDownCaretLabel?.isHidden = !show
|
|
||||||
dropDownCarrotWidth?.isActive = !show
|
|
||||||
setNeedsLayout()
|
|
||||||
layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func formIsEnabled() {
|
|
||||||
|
|
||||||
// Set outside the dispatch so that registerAnimations can know about it
|
|
||||||
isEnabled = true
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
self.isUserInteractionEnabled = true
|
|
||||||
self.formDescriptionLabel?.textColor = UIColor.mfBattleshipGrey()
|
|
||||||
self.placeholderErrorLabel?.textColor = .black
|
|
||||||
self.separatorView?.backgroundColor = (self.showError) ? UIColor.mfPumpkin() : .black
|
|
||||||
self.showDropDown(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func formIsDisabled() {
|
|
||||||
|
|
||||||
// Set outside the dispatch so that registerAnimations can know about it
|
|
||||||
isEnabled = false
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
self.isUserInteractionEnabled = false
|
|
||||||
self.formDescriptionLabel?.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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open func showPlaceholderErrorLabel(_ show: Bool) {
|
open func showPlaceholderErrorLabel(_ show: Bool) {
|
||||||
|
|
||||||
placeholderErrorLabel?.isHidden = !show
|
feedbackLabel?.isHidden = !show
|
||||||
}
|
}
|
||||||
|
|
||||||
open func showDashSeperatorView(_ dash: Bool) {
|
open func showDashSeperatorView(_ dash: Bool) {
|
||||||
@ -433,7 +338,31 @@ extension FormEntryField {
|
|||||||
|
|
||||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||||
setWithMap(json)
|
|
||||||
|
guard let dictionary = json,
|
||||||
|
!dictionary.isEmpty
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
if let formText = dictionary[KeyLabel] as? String {
|
||||||
|
self.descriptionText = formText
|
||||||
|
}
|
||||||
|
|
||||||
|
if let text = dictionary[KeyDisable] as? String, text.isEqual(StringY) || dictionary.boolForKey(KeyDisable) {
|
||||||
|
isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if let errMessage = dictionary[KeyErrorMessage] as? String {
|
||||||
|
self.errorMessage = errMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
if let hideBorder = dictionary["hideBorder"] as? Bool {
|
||||||
|
self.hideBorder = hideBorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key used to send text value to server
|
||||||
|
if let fieldKey = dictionary[KeyFieldKey] as? String {
|
||||||
|
self.fieldKey = fieldKey
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||||
|
|||||||
@ -24,8 +24,7 @@ import UIKit
|
|||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
public var textField: UITextField?
|
private(set) var textField: UITextField?
|
||||||
|
|
||||||
private var calendar: Calendar?
|
private var calendar: Calendar?
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -52,18 +51,27 @@ import UIKit
|
|||||||
/// If you're using a MFViewController, you must set this to it
|
/// If you're using a MFViewController, you must set this to it
|
||||||
public weak var uiTextFieldDelegate: UITextFieldDelegate? {
|
public weak var uiTextFieldDelegate: UITextFieldDelegate? {
|
||||||
get { return textField?.delegate }
|
get { return textField?.delegate }
|
||||||
set {
|
set { textField?.delegate = newValue }
|
||||||
textField?.delegate = newValue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public var enabledTextColor: UIColor?
|
||||||
|
public var disabledTextColor: UIColor?
|
||||||
|
|
||||||
public var observingForChanges = false
|
public var observingForChanges = false
|
||||||
|
|
||||||
private var borderPath: UIBezierPath?
|
public override var isEnabled: Bool {
|
||||||
|
didSet {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
self.textField?.textColor = self.isEnabled ? self.enabledTextColor : self.enabledTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The text of this textField.
|
// The text of this textField.
|
||||||
public override var text: String? {
|
public override var text: String? {
|
||||||
@ -74,37 +82,7 @@ import UIKit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override var formText: String? {
|
public var validationBlock: ((_ value: String?) -> Bool)? {
|
||||||
get { return formDescriptionLabel?.text }
|
|
||||||
set {
|
|
||||||
formDescriptionLabel?.text = newValue
|
|
||||||
setAccessibilityString(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets placeholder text in the textField.
|
|
||||||
public override 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 validationBlock: ((_ enteredValue: String?) -> Bool)? {
|
|
||||||
didSet {
|
didSet {
|
||||||
valueChanged()
|
valueChanged()
|
||||||
}
|
}
|
||||||
@ -126,7 +104,7 @@ import UIKit
|
|||||||
|
|
||||||
required public init?(coder: NSCoder) {
|
required public init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("TextEntryField does not support xib.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
|
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
|
||||||
@ -136,17 +114,6 @@ import UIKit
|
|||||||
setBothTextFieldDelegates(bothDelegates)
|
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()
|
|
||||||
dropDownCaretLabel?.isHidden = hasDropDown
|
|
||||||
self.hasDropDown = !hasDropDown
|
|
||||||
setWithMap(map, bothDelegates: bothDelegates)
|
|
||||||
}
|
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -170,25 +137,6 @@ import UIKit
|
|||||||
textField.topAnchor.constraint(equalTo: container.topAnchor, constant: 10),
|
textField.topAnchor.constraint(equalTo: container.topAnchor, constant: 10),
|
||||||
textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
|
textField.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 16),
|
||||||
container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 10)])
|
container.bottomAnchor.constraint(equalTo: textField.bottomAnchor, constant: 10)])
|
||||||
|
|
||||||
let dropDownCaretLabel = Label()
|
|
||||||
self.dropDownCaretLabel = dropDownCaretLabel
|
|
||||||
dropDownCaretLabel.setContentHuggingPriority(UILayoutPriority(900), for: .horizontal)
|
|
||||||
dropDownCaretLabel.setContentHuggingPriority(UILayoutPriority(251), for: .vertical)
|
|
||||||
dropDownCaretLabel.setContentCompressionResistancePriority(UILayoutPriority(900), for: .horizontal)
|
|
||||||
dropDownCaretLabel.isHidden = true
|
|
||||||
dropDownCaretLabel.isUserInteractionEnabled = true
|
|
||||||
let tapOnCarrot = UITapGestureRecognizer(target: self, action: #selector(startEditing))
|
|
||||||
dropDownCaretLabel.addGestureRecognizer(tapOnCarrot)
|
|
||||||
|
|
||||||
container.addSubview(dropDownCaretLabel)
|
|
||||||
|
|
||||||
dropDownCaretLabel.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
|
|
||||||
dropDownCaretLabel.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 6).isActive = true
|
|
||||||
container.trailingAnchor.constraint(equalTo: dropDownCaretLabel.trailingAnchor, constant: 16).isActive = true
|
|
||||||
container.bottomAnchor.constraint(equalTo: dropDownCaretLabel.bottomAnchor).isActive = true
|
|
||||||
dropDownCarrotWidth = dropDownCaretLabel.widthAnchor.constraint(equalToConstant: 0)
|
|
||||||
dropDownCarrotWidth?.isActive = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func updateView(_ size: CGFloat) {
|
open override func updateView(_ size: CGFloat) {
|
||||||
@ -206,31 +154,20 @@ import UIKit
|
|||||||
uiTextFieldDelegate = nil
|
uiTextFieldDelegate = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func draw(_ rect: CGRect) {
|
|
||||||
super.draw(rect)
|
|
||||||
|
|
||||||
borderPath?.removeAllPoints()
|
|
||||||
|
|
||||||
if !hideBorder, let frame = fieldContainer?.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
|
// MARK: - Methods
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
|
|
||||||
|
public func showDropDown(_ show: Bool) {
|
||||||
|
|
||||||
|
if hasDropDown {
|
||||||
|
dropDownCaretLabel?.isHidden = !show
|
||||||
|
dropDownCarrotWidth?.isActive = !show
|
||||||
|
setNeedsLayout()
|
||||||
|
layoutIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open override func showErrorMessage(_ errorMessage: String?) {
|
open override func showErrorMessage(_ errorMessage: String?) {
|
||||||
super.showErrorMessage(errorMessage)
|
super.showErrorMessage(errorMessage)
|
||||||
|
|
||||||
@ -327,6 +264,7 @@ import UIKit
|
|||||||
|
|
||||||
textField?.isUserInteractionEnabled = true
|
textField?.isUserInteractionEnabled = true
|
||||||
textField?.isEnabled = true
|
textField?.isEnabled = true
|
||||||
|
showDropDown(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
open override func formIsDisabled() {
|
open override func formIsDisabled() {
|
||||||
@ -334,6 +272,7 @@ import UIKit
|
|||||||
|
|
||||||
textField?.isUserInteractionEnabled = false
|
textField?.isUserInteractionEnabled = false
|
||||||
textField?.isEnabled = false
|
textField?.isEnabled = false
|
||||||
|
self.showDropDown(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------
|
//--------------------------------------------------
|
||||||
@ -344,7 +283,7 @@ import UIKit
|
|||||||
|
|
||||||
// Update label for placeholder
|
// Update label for placeholder
|
||||||
if !showError {
|
if !showError {
|
||||||
placeholderErrorLabel?.text = ""
|
feedbackLabel?.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
let previousValidity = isValid
|
let previousValidity = isValid
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user