This commit is contained in:
Kevin G Christiano 2019-11-21 11:47:12 -05:00
parent 26c73070d9
commit 88442fe59c
13 changed files with 361 additions and 355 deletions

View File

@ -42,10 +42,10 @@
0A21DB94235E24ED00C160A2 /* DigitEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A21DB93235E24ED00C160A2 /* DigitEntryField.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 */; };
0A6BF4722360C56C0028F841 /* DropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */; };
0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */; };
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; };
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
0ABD136B237B193A0081388D /* FormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD136A237B193A0081388D /* FormView.swift */; };
0ABD136B237B193A0081388D /* FormFieldContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD136A237B193A0081388D /* FormFieldContainer.swift */; };
0ABD136D237CAD1E0081388D /* DateDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */; };
0ABD1371237DB0450081388D /* ItemDropdownEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */; };
0AE14F64238315D2005417F8 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14F63238315D2005417F8 /* TextField.swift */; };
@ -236,12 +236,12 @@
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>"; };
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>"; };
0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseDropdownEntryField.swift; sourceTree = "<group>"; };
0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.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>"; };
0A8321AE2355FE9500CB7F00 /* DigitBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigitBox.swift; sourceTree = "<group>"; };
0ABD136A237B193A0081388D /* FormView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormView.swift; sourceTree = "<group>"; };
0ABD136A237B193A0081388D /* FormFieldContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormFieldContainer.swift; sourceTree = "<group>"; };
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateDropdownEntryField.swift; sourceTree = "<group>"; };
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDropdownEntryField.swift; sourceTree = "<group>"; };
0AE14F63238315D2005417F8 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = "<group>"; };
@ -452,7 +452,7 @@
0ABD1369237B18EE0081388D /* views */ = {
isa = PBXGroup;
children = (
0ABD136A237B193A0081388D /* FormView.swift */,
0ABD136A237B193A0081388D /* FormFieldContainer.swift */,
);
path = views;
sourceTree = "<group>";
@ -809,7 +809,7 @@
0A21DB7E235DECC500C160A2 /* EntryField.swift */,
0A21DB82235DFBC500C160A2 /* MdnEntryField.swift */,
0A21DB93235E24ED00C160A2 /* DigitEntryField.swift */,
0A6BF4712360C56C0028F841 /* DropdownEntryField.swift */,
0A6BF4712360C56C0028F841 /* BaseDropdownEntryField.swift */,
0ABD136C237CAD1E0081388D /* DateDropdownEntryField.swift */,
0ABD1370237DB0450081388D /* ItemDropdownEntryField.swift */,
);
@ -1187,14 +1187,14 @@
D29770FC21F7C77400B2F0D0 /* MVMCoreUITextFieldView.m in Sources */,
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
0A6BF4722360C56C0028F841 /* DropdownEntryField.swift in Sources */,
0A6BF4722360C56C0028F841 /* BaseDropdownEntryField.swift in Sources */,
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */,
0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */,
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */,
D29DF2BE21E7BEA4003B2FB9 /* TopTabbar.m in Sources */,
0ABD136B237B193A0081388D /* FormView.swift in Sources */,
0ABD136B237B193A0081388D /* FormFieldContainer.swift in Sources */,
D2A514632213643100345BFB /* MoleculeStackCenteredTemplate.swift in Sources */,
D29DF32421ED0DA2003B2FB9 /* TextButtonView.m in Sources */,
D29DF29E21E7AE3B003B2FB9 /* MFStyler.m in Sources */,

View File

@ -1,5 +1,5 @@
//
// DropdownEntryField.swift
// BaseDropdownEntryField.swift
// MVMCoreUI
//
// Created by Kevin Christiano on 10/23/19.
@ -9,10 +9,10 @@
import UIKit
/**
This class is intended to be subclassed.
See ItemDropdownEntryField and DateDropdownEntryField.
This class is intended to be subclassed.
See ItemDropdownEntryField and DateDropdownEntryField.
*/
@objcMembers open class DropdownEntryField: TextEntryField {
@objcMembers open class BaseDropdownEntryField: TextEntryField {
//--------------------------------------------------
// MARK: - Outlets
//--------------------------------------------------
@ -31,9 +31,11 @@ import UIKit
// MARK: - Property Observers
//--------------------------------------------------
public override var isEnabled: Bool {
didSet {
dropDownCaretView.isEnabled = isEnabled
@objc public override var isEnabled: Bool {
get { super.isEnabled }
set (enabled) {
dropDownCaretView.isEnabled = enabled
super.isEnabled = enabled
}
}
@ -41,11 +43,11 @@ import UIKit
// MARK: - Initializers
//--------------------------------------------------
public override init(frame: CGRect) {
@objc public override init(frame: CGRect) {
super.init(frame: frame)
}
required public init?(coder: NSCoder) {
@objc required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("DropdownEntryField does not support xib.")
}
@ -54,7 +56,7 @@ import UIKit
// MARK: - Setup
//--------------------------------------------------
public override func setupFieldContainerContent(_ container: UIView) {
@objc public override func setupFieldContainerContent(_ container: UIView) {
super.setupFieldContainerContent(container)
container.addSubview(dropDownCaretView)
@ -66,7 +68,6 @@ import UIKit
container.trailingAnchor.constraint(equalTo: dropDownCaretView.trailingAnchor, constant: 16).isActive = true
container.bottomAnchor.constraint(greaterThanOrEqualTo: dropDownCaretView.bottomAnchor, constant: 13).isActive = true
dropDownCaretView.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
let caretTap = UITapGestureRecognizer(target: self, action: #selector(startEditing))
@ -75,15 +76,13 @@ import UIKit
}
// MARK: - Molecular
extension DropdownEntryField {
extension BaseDropdownEntryField {
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json, !dictionary.isEmpty else { return }
if let _ = dictionary[KeyType] as? String {
dropDownCaretView.isHidden = false
}
dropDownCaretView.setWithJSON(dictionary, delegateObject: delegateObject, additionalData: additionalData)
}
}

View File

@ -9,7 +9,7 @@
import UIKit
open class DateDropdownEntryField: DropdownEntryField {
@objcMembers open class DateDropdownEntryField: BaseDropdownEntryField {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -32,44 +32,41 @@ open class DateDropdownEntryField: DropdownEntryField {
return formatter
}()
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
/// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well.
private weak var proprietorTextDelegate: UITextFieldDelegate?
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public convenience init() {
self.init(frame: .zero)
@objc public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public convenience init(startDate: Date, endDate: Date, showStartDate: Bool = true) {
@objc public convenience init() {
self.init(frame: .zero)
}
@objc public convenience init(startDate: Date, endDate: Date, showStartDate: Bool = true) {
self.init(frame: .zero)
setup()
setDatePickerDuration(from: startDate, to: endDate, showStartDate: showStartDate)
}
private func setup() {
@objc required public init?(coder: NSCoder) {
fatalError("DateDropdownEntryField init(coder:) has not been implemented")
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
@objc private func setup() {
datePicker = MVMCoreUICommonViewsUtility.addDatePicker(to: textField)
datePicker?.addTarget(self, action: #selector(pickerValueChanged), for: .valueChanged)
datePicker?.timeZone = NSTimeZone.system
MVMCoreUICommonViewsUtility.addDismissToolbar(textField, delegate: self)
uiTextFieldDelegate = self
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
public func setDatePickerDuration(from startDate: Date?, to endDate: Date?, showStartDate: Bool = true) {
@objc public func setDatePickerDuration(from startDate: Date?, to endDate: Date?, showStartDate: Bool = true) {
datePicker?.minimumDate = startDate
datePicker?.maximumDate = endDate
@ -79,7 +76,7 @@ open class DateDropdownEntryField: DropdownEntryField {
}
}
public func dismissDatePicker() -> Date? {
@objc public func dismissDatePicker() -> Date? {
let pickedDate = datePicker?.date
setTextWith(date: pickedDate)
@ -88,8 +85,8 @@ open class DateDropdownEntryField: DropdownEntryField {
return pickedDate
}
private func setTextWith(date: Date?) {
@objc private func setTextWith(date: Date?) {
guard let date = date else { return }
if calendar.isDate(date, inSameDayAs: Date()) {
@ -104,26 +101,16 @@ open class DateDropdownEntryField: DropdownEntryField {
super.dismissFieldInput(sender)
}
@objc func pickerValueChanged(_ sender: UIDatePicker){
setTextWith(date: datePicker?.date)
}
}
// MARK: - UITextField Intercept
extension DateDropdownEntryField {
public func textFieldDidBeginEditing(_ textField: UITextField) {
@objc func pickerValueChanged(_ sender: UIDatePicker) {
isSelected = true
proprietorTextDelegate?.textFieldDidBeginEditing?(textField)
setTextWith(date: datePicker?.date)
}
}
// MARK: - Molecular
extension DateDropdownEntryField {
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json, !dictionary.isEmpty else { return }

View File

@ -8,9 +8,8 @@
import UIKit
@objc protocol DigitBoxDelegate: NSObjectProtocol {
@objc protocol DigitBoxDelegate {
@objc optional func digitFieldDidDelete(_ textField: UITextField?)
@objc optional func textFieldDidChange(_ textField: UITextField)
}
@ -40,11 +39,12 @@ import UIKit
//--------------------------------------------------
public override var showError: Bool {
didSet {
get { return super.showError }
set (error) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.borderStrokeColor = self.showError ? .mfPumpkin() : .mfSilver()
self.borderStrokeColor = error ? .mfPumpkin() : .mfSilver()
let barHeight: CGFloat = self.showError ? 4 : 1
self.bottomBar?.frame = CGRect(x: 0, y: self.bounds.height - barHeight, width: self.bounds.width, height: barHeight)
@ -53,6 +53,7 @@ import UIKit
self.setNeedsDisplay()
self.layoutIfNeeded()
}
super.showError = error
}
}
@ -142,16 +143,7 @@ import UIKit
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isBackspace {
digitBoxDelegate?.digitFieldDidDelete?(self.digitField)
}
return true
}
public override func updateView(_ size: CGFloat) {
super.updateView(size)
@ -188,13 +180,3 @@ import UIKit
}
}
}
// TODO: Move if working properly.
extension String {
var isBackspace: Bool {
let char = self.cString(using: String.Encoding.utf8)!
return strcmp(char, "\\b") == -92
}
}

View File

@ -27,14 +27,15 @@ import UIKit
//--------------------------------------------------
public override var isEnabled: Bool {
didSet {
titleLabel.textColor = self.isEnabled ? .mfBattleshipGrey() : .mfSilver()
get { return super.isEnabled }
set (enabled) {
titleLabel.textColor = enabled ? .mfBattleshipGrey() : .mfSilver()
digitBoxes.forEach {
$0.isEnabled = self.isEnabled
$0.isUserInteractionEnabled = isEnabled
$0.digitField.isEnabled = isEnabled
$0.digitField.textColor = isEnabled ? .black : .mfBattleshipGrey()
$0.isEnabled = enabled
$0.isUserInteractionEnabled = enabled
$0.digitField.isEnabled = enabled
$0.digitField.textColor = enabled ? .black : .mfBattleshipGrey()
}
}
}
@ -46,8 +47,9 @@ import UIKit
}
public override var isLocked: Bool {
didSet {
digitBoxes.forEach { $0.isLocked = self.isLocked }
get { return super.isLocked }
set (locked) {
digitBoxes.forEach { $0.isLocked = locked }
}
}
@ -127,25 +129,20 @@ import UIKit
// MARK: - Initializers
//--------------------------------------------------
public override init(frame: CGRect) {
@objc public override init(frame: CGRect) {
super.init(frame: frame)
}
public convenience init() {
@objc public convenience init() {
self.init(frame: .zero)
}
public convenience init(numberOfDigits: Int) {
@objc public convenience init(numberOfDigits: Int) {
self.init(frame: .zero)
self.numberOfDigits = numberOfDigits
}
public init(numberOfDigits: Int, bothDelegates delegate: (UITextFieldDelegate & ObservingTextFieldDelegate)?, size: CGFloat? = nil) {
self.numberOfDigits = numberOfDigits
super.init(bothDelegates: delegate)
}
required public init?(coder: NSCoder) {
@objc required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("DigitEntryField xib has not been implemented")
}
@ -154,15 +151,15 @@ import UIKit
// MARK: - Setup
//--------------------------------------------------
public override func setupFieldContainerContent(_ container: UIView) {
@objc public override func setupFieldContainerContent(_ container: UIView) {
alignCenterHorizontal()
isAccessibilityElement = false
entryContainer.disableBorders = true
entryContainer.disableAllBorders = true
assembleDigitFieldsView(size: MVMCoreUISplitViewController.getDetailViewWidth())
}
private func createDigitField() -> DigitBox {
@objc private func createDigitField() -> DigitBox {
let digitBox = DigitBox()
digitBox.isAccessibilityElement = true
@ -172,7 +169,7 @@ import UIKit
return digitBox
}
func assembleDigitFieldsView(size: CGFloat) {
@objc func assembleDigitFieldsView(size: CGFloat) {
var accessibleElements: [Any] = [titleLabel]
@ -218,10 +215,10 @@ import UIKit
// MARK: - Lifecycle
//--------------------------------------------------
open override func updateView(_ size: CGFloat) {
@objc open override func updateView(_ size: CGFloat) {
super.updateView(size)
entryContainer.disableBorders = true
entryContainer.disableAllBorders = true
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@ -234,10 +231,10 @@ import UIKit
}
}
open override func reset() {
@objc open override func reset() {
super.reset()
entryContainer.disableBorders = false
entryContainer.disableAllBorders = false
digitBoxes.forEach { $0.reset() }
}
@ -245,7 +242,7 @@ import UIKit
// MARK: - Methods
//--------------------------------------------------
public func setAsSecureTextEntry(_ secureEntry: Bool) {
@objc public func setAsSecureTextEntry(_ secureEntry: Bool) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@ -259,7 +256,7 @@ import UIKit
}
}
public override func defaultValidationBlock() {
@objc public override func defaultValidationBlock() {
validationBlock = { enteredValue in
guard let enteredValue = enteredValue else { return false }
@ -268,7 +265,7 @@ import UIKit
}
}
public func selectPreviousDigitField(_ currentTextField: UITextField?, clear: Bool) {
@objc public func selectPreviousDigitField(_ currentTextField: UITextField?, clear: Bool) {
var selectPreviousField = false
@ -292,7 +289,7 @@ import UIKit
}
}
public func selectNextDigitField(_ currentTextField: UITextField?, clear: Bool) {
@objc public func selectNextDigitField(_ currentTextField: UITextField?, clear: Bool) {
var selectNextField = false
@ -326,7 +323,7 @@ import UIKit
selectedDigitField?.digitField.becomeFirstResponder()
}
override open func resignFirstResponder() -> Bool {
@objc override open func resignFirstResponder() -> Bool {
selectedDigitField?.digitField.resignFirstResponder()
selectedDigitField?.isSelected = false
@ -343,7 +340,7 @@ import UIKit
}
}
open class func getEnabledDigitFields(_ textFieldToDetermine: [DigitBox]) -> [TextField]? {
@objc open class func getEnabledDigitFields(_ textFieldToDetermine: [DigitBox]) -> [TextField]? {
return textFieldToDetermine.filter { $0.isEnabled }.compactMap { $0.digitField }
}
@ -352,7 +349,6 @@ import UIKit
// MARK: - TextField Delegate
extension DigitEntryField {
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
@ -360,12 +356,7 @@ extension DigitEntryField {
return proprietorTextDelegate?.textFieldShouldReturn?(textField) ?? true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isBackspace {
selectPreviousDigitField(textField, clear: true)
return true
}
@objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) {
return false
@ -400,7 +391,7 @@ extension DigitEntryField {
return false
}
func digitFieldDidDelete(_ textField: UITextField?) {
@objc func digitFieldDidDelete(_ textField: UITextField?) {
// Empty cell, go back to previous cell and clear.
selectPreviousDigitField(textField, clear: true)
@ -454,7 +445,7 @@ extension DigitEntryField {
// MARK: - Molecular
extension DigitEntryField {
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json else { return }
@ -473,7 +464,7 @@ extension DigitEntryField {
}
}
open override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
@objc open override class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 115
}
}

View File

@ -49,83 +49,97 @@ import UIKit
weak var delegateObject: MVMCoreUIDelegateObject?
//--------------------------------------------------
// MARK: - Properties
// MARK: - Stored Properties
//--------------------------------------------------
public var isValid = false
public var isValid: Bool = false
public var fieldKey: String?
public var errorMessage: String?
/// Determines whther the feedback label will clear itself after user interaction or display update.
// public var fixedFeedback: Bool = false
private var _isEnabled: Bool = true
private var _showError: Bool = false
private var _isLocked: Bool = false
private var _isSelected: Bool = false
//--------------------------------------------------
// MARK: - Property Observers
// MARK: - Computed Properties
//--------------------------------------------------
/// Toggles error or original UI.
public var showError = false {
willSet {
isLocked = false
isSelected = false
isEnabled = false
}
didSet {
/// Toggles enabled (original) or disabled UI.
public var isEnabled: Bool {
get { return _isEnabled }
set (enabled) {
_isEnabled = enabled
_isLocked = false
_isSelected = false
_showError = false
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.entryContainer.showError = self.showError
self.feedback = self.showError ? self.errorMessage : nil
self.entryContainer.isEnabled = enabled
self.feedbackLabel.textColor = enabled ? .black : .mfSilver()
self.titleLabel.textColor = enabled ? .mfBattleshipGrey() : .mfSilver()
}
}
}
/// Toggles enabled (original) or disabled UI.
public var isEnabled = true {
willSet {
isLocked = false
isSelected = false
showError = false
}
didSet {
/// Toggles error or original UI.
public var showError: Bool {
get { return _showError }
set (error) {
_showError = error
_isLocked = false
_isSelected = false
_isEnabled = true
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.isUserInteractionEnabled = self.isEnabled
self.entryContainer.isEnabled = self.isEnabled
self.feedbackLabel.textColor = self.isEnabled ? .black : .mfSilver()
self.titleLabel.textColor = self.isEnabled ? .mfBattleshipGrey() : .mfSilver()
self.entryContainer.showError = error
self.feedback = error ? self.errorMessage : nil
}
}
}
/// Toggles original or locked UI.
public var isLocked = false {
willSet {
isEnabled = true
isSelected = false
showError = false
}
didSet {
public var isLocked: Bool {
get { return _isLocked }
set (locked) {
_isLocked = locked
_isEnabled = true
_isSelected = false
_showError = false
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.isUserInteractionEnabled = !self.isLocked
self.entryContainer.isLocked = self.isLocked
self.entryContainer.isLocked = locked
}
}
}
/// Toggles selected or original (unselected) UI.
public var isSelected = false {
willSet {
isLocked = false
isEnabled = true
showError = false
}
didSet {
public var isSelected: Bool {
get { return _isSelected }
set (selected) {
_isSelected = selected
_isLocked = false
_isEnabled = true
_showError = false
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.entryContainer.isSelected = self.isSelected
self.entryContainer.isSelected = selected
}
}
}
@ -137,9 +151,9 @@ import UIKit
/// Sets the text of titleLabel
public var title: String? {
get { return titleLabel.text }
set {
titleLabel.text = newValue
setAccessibilityString(newValue)
set (newText) {
titleLabel.text = newText
setAccessibilityString(newText)
}
}
@ -152,9 +166,9 @@ import UIKit
/// Sets feedback text in the textField.
public var feedback: String? {
get { return feedbackLabel.text }
set {
feedbackLabel.text = newValue
setAccessibilityString(newValue)
set (newFeedback) {
feedbackLabel.text = newFeedback
setAccessibilityString(newFeedback)
entryContainer.refreshUI()
}
}
@ -180,21 +194,21 @@ import UIKit
//--------------------------------------------------
/// This must be overriden by a subclass.
public override init(frame: CGRect) {
@objc public override init(frame: CGRect) {
super.init(frame: frame)
}
public convenience init() {
@objc public convenience init() {
self.init(frame: .zero)
}
public init(title: String) {
@objc public init(title: String) {
super.init(frame: .zero)
titleLabel.text = title
}
required public init?(coder: NSCoder) {
@objc required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("EntryField does not support xib.")
}
@ -204,7 +218,7 @@ import UIKit
//--------------------------------------------------
/// Initial configuration of class and view.
final public override func setupView() {
@objc final public override func setupView() {
guard subviews.isEmpty else { return }
@ -243,7 +257,7 @@ import UIKit
layoutMarginsGuide.bottomAnchor.constraint(equalTo: feedbackLabel.bottomAnchor).isActive = true
}
open override func layoutSubviews() {
@objc open override func layoutSubviews() {
super.layoutSubviews()
entryContainer.refreshUI()
@ -251,11 +265,11 @@ import UIKit
/// Method to override.
/// Intended to add the interactive content (i.e. textField) to the entryContainer.
open func setupFieldContainerContent(_ container: UIView) {
@objc open func setupFieldContainerContent(_ container: UIView) {
// To be overridden by subclass.
}
open override func updateView(_ size: CGFloat) {
@objc open override func updateView(_ size: CGFloat) {
super.updateView(size)
titleLabel.updateView(size)
@ -263,13 +277,18 @@ import UIKit
entryContainer.updateView(size)
}
open override func reset() {
@objc open override func reset() {
super.reset()
isEnabled = true
_isLocked = false
_isSelected = false
_showError = false
backgroundColor = .clear
titleLabel.reset()
feedbackLabel.reset()
entryContainer.subviews.forEach { $0.removeFromSuperview() }
entryContainer.reset()
titleLabel.textColor = .mfBattleshipGrey()
}
@ -277,14 +296,14 @@ import UIKit
// MARK: - Constraint Methods
//--------------------------------------------------
open override func setLeftPinConstant(_ constant: CGFloat) {
@objc open override func setLeftPinConstant(_ constant: CGFloat) {
entryContainerLeading?.constant = constant
feedbackLabelLeading?.constant = constant
titleLabelLeading?.constant = constant
}
open override func setRightPinConstant(_ constant: CGFloat) {
@objc open override func setRightPinConstant(_ constant: CGFloat) {
entryContainerTrailing?.constant = constant
feedbackLabelTrailing?.constant = constant
@ -295,7 +314,7 @@ import UIKit
// MARK: - Molecular
extension EntryField {
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
self.delegateObject = delegateObject
@ -303,7 +322,7 @@ extension EntryField {
entryContainer.setWithJSON(dictionary, delegateObject: delegateObject, additionalData: additionalData)
if let titleText = dictionary["title"] as? String {
if let titleText = dictionary[KeyTitle] as? String {
title = titleText
}
@ -319,13 +338,17 @@ extension EntryField {
self.isLocked = isLocked
}
if let isSelected = dictionary["isSelected"] as? Bool {
self.isSelected = isSelected
}
// 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 {
@objc override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
return 115
}
}
@ -333,15 +356,15 @@ extension EntryField {
// MARK: - Form Validation
extension EntryField: FormValidationProtocol {
public func isValidField() -> Bool {
@objc public func isValidField() -> Bool {
return isValid
}
public func formFieldName() -> String? {
@objc public func formFieldName() -> String? {
return fieldKey
}
public func formFieldValue() -> Any? {
@objc public func formFieldValue() -> Any? {
return text
}
}

View File

@ -9,7 +9,7 @@
import UIKit
open class ItemDropdownEntryField: DropdownEntryField {
open class ItemDropdownEntryField: BaseDropdownEntryField {
//--------------------------------------------------
// MARK: - Properties
//--------------------------------------------------
@ -19,59 +19,49 @@ open class ItemDropdownEntryField: DropdownEntryField {
public var componentsCount = 1
/// When selecting first responder, allow initial selected value to appear in empty text field.
/// When selecting for first responder, allow initial selected value to appear in empty text field.
public var setInitialValueInTextField = true
//--------------------------------------------------
// MARK: - Delegate
//--------------------------------------------------
/// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well.
private weak var proprietorTextDelegate: UITextFieldDelegate?
/// If you're using a MFViewController, you must set this to it
public override weak var uiTextFieldDelegate: UITextFieldDelegate? {
get { return textField.delegate }
set {
textField.delegate = self
proprietorTextDelegate = newValue
}
}
//--------------------------------------------------
// MARK: - Initializer
//--------------------------------------------------
public convenience init() {
self.init(frame: .zero)
@objc public override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
public convenience init(pickerData: [String]) {
@objc public convenience init() {
self.init(frame: .zero)
}
@objc public convenience init(pickerData: [String]) {
self.init(frame: .zero)
self.pickerData = pickerData
setup()
}
private func setup() {
pickerView = MVMCoreUICommonViewsUtility.addPicker(to: textField, delegate: self)
textField.hideBlinkingCaret = true
uiTextFieldDelegate = self
@objc required public init?(coder: NSCoder) {
fatalError("ItemDropdownEntryField init(coder:) has not been implemented")
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
public func setPickerDelegates(delegate: UIPickerViewDelegate & UIPickerViewDataSource) {
@objc private func setup() {
pickerView = MVMCoreUICommonViewsUtility.addPicker(to: textField, delegate: self)
textField.hideBlinkingCaret = true
uiTextFieldDelegate = self
}
@objc public func setPickerDelegates(delegate: UIPickerViewDelegate & UIPickerViewDataSource) {
pickerView?.delegate = delegate
pickerView?.dataSource = delegate
}
private func setInitialValueFromPicker() {
@objc private func setInitialValueFromPicker() {
if setInitialValueInTextField, let pickerIndex = pickerView?.selectedRow(inComponent: 0) {
text = pickerData[pickerIndex]
@ -88,19 +78,19 @@ open class ItemDropdownEntryField: DropdownEntryField {
// MARK:- Base Picker Delegate
extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
@objc public func numberOfComponents(in pickerView: UIPickerView) -> Int {
return componentsCount
}
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
@objc public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return pickerData.count
}
public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
@objc public func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return pickerData[row]
}
public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
@objc public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
text = pickerData[row]
}
}
@ -108,7 +98,7 @@ extension ItemDropdownEntryField: UIPickerViewDelegate, UIPickerViewDataSource {
// MARK: - Molecular
extension ItemDropdownEntryField {
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@objc override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let dictionary = json, !dictionary.isEmpty else { return }
@ -119,20 +109,10 @@ extension ItemDropdownEntryField {
}
}
// MARK: - UITextField Intercept
// MARK: - Accessibility
extension ItemDropdownEntryField {
public func textFieldDidBeginEditing(_ textField: UITextField) {
setInitialValueFromPicker()
proprietorTextDelegate?.textFieldDidBeginEditing?(textField)
}
}
// MARK: - Accessibility
extension DropdownEntryField {
open override func setAccessibilityString(_ accessibilityString: String?) {
@objc open override func setAccessibilityString(_ accessibilityString: String?) {
var accessibilityString = accessibilityString ?? ""

View File

@ -16,7 +16,7 @@ import MVMCore
*/
@objcMembers open class MdnEntryField: TextEntryField, ABPeoplePickerNavigationControllerDelegate, CNContactPickerDelegate {
//--------------------------------------------------
// MARK: - Properties
// MARK: - Stored Properties
//--------------------------------------------------
public var isNationalMDN = true
@ -30,7 +30,7 @@ import MVMCore
private weak var proprietorTextDelegate: UITextFieldDelegate?
//--------------------------------------------------
// MARK: - Property Observers
// MARK: - Computed Properties
//--------------------------------------------------
/// Formats the MDN when setting and removes format of MDN when reading.
@ -52,21 +52,15 @@ import MVMCore
// MARK: - Initializers
//--------------------------------------------------
public override init(frame: CGRect) {
@objc public override init(frame: CGRect) {
super.init(frame: .zero)
}
public convenience init() {
@objc public convenience init() {
self.init(frame: .zero)
}
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
public override init(bothDelegates: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
super.init(frame: .zero)
setBothTextDelegates(to: bothDelegates)
}
required public init?(coder: NSCoder) {
@objc required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("MdnEntryField xib not supported.")
}
@ -75,7 +69,7 @@ import MVMCore
// MARK: - Setup
//--------------------------------------------------
public override func setupFieldContainerContent(_ container: UIView) {
@objc public override func setupFieldContainerContent(_ container: UIView) {
super.setupFieldContainerContent(container)
textField.keyboardType = .numberPad
@ -92,7 +86,7 @@ import MVMCore
// MARK: - Methods
//--------------------------------------------------
public func hasValidMDN() -> Bool {
@objc public func hasValidMDN() -> Bool {
guard let MDN = mdn, !MDN.isEmpty else { return true }
@ -103,13 +97,13 @@ import MVMCore
return MVMCoreUIUtility.validateInternationalMDNString(MDN)
}
public func validateAndColor() -> Bool {
@objc public func validateAndColor() -> Bool {
if !shouldValidateMDN {
let isValid = hasValidMDN()
if isValid {
clearErrorState()
showError = false
} else {
errorMessage = errorMessage ?? MVMCoreUIUtility.hardcodedString(withKey: "textfield_phone_format_error_message")
showError = true
@ -136,7 +130,7 @@ import MVMCore
// MARK: - Contact Picker Delegate
//--------------------------------------------------
public func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
@objc public func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
if let phoneNumber = contactProperty.value as? CNPhoneNumber {
@ -163,14 +157,14 @@ import MVMCore
// MARK: - Implemented TextField Delegate
//--------------------------------------------------
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return proprietorTextDelegate?.textFieldShouldReturn?(textField) ?? true
}
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
@objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) {
return false
@ -179,13 +173,13 @@ import MVMCore
return proprietorTextDelegate?.textField?(textField, shouldChangeCharactersIn: range, replacementString: string) ?? true
}
public func textFieldDidBeginEditing(_ textField: UITextField) {
@objc public func textFieldDidBeginEditing(_ textField: UITextField) {
textField.text = MVMCoreUIUtility.removeMdnFormat(textField.text)
proprietorTextDelegate?.textFieldDidBeginEditing?(textField)
}
public func textFieldDidEndEditing(_ textField: UITextField) {
@objc public func textFieldDidEndEditing(_ textField: UITextField) {
proprietorTextDelegate?.textFieldDidEndEditing?(textField)
@ -194,21 +188,17 @@ import MVMCore
}
}
//--------------------------------------------------
// MARK: - Passed Along TextField delegate
//--------------------------------------------------
public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
}
public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
}
public func textFieldShouldClear(_ textField: UITextField) -> Bool {
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
return proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
}

View File

@ -36,30 +36,41 @@ import UIKit
}()
//--------------------------------------------------
// MARK: - Properties
// MARK: - Stored Properties
//--------------------------------------------------
/// Set enabled and disabled colors to be utilized when setting this texfield's isEnabled property.
public var textColor: (enabled: UIColor?, disabled: UIColor?) = (.black, .mfSilver())
public var observingForChange = false
public var observingForChange: Bool = false
//--------------------------------------------------
// MARK: - Property Observers
// MARK: - Computed Properties
//--------------------------------------------------
public override var isEnabled: Bool {
didSet {
get { return super.isEnabled }
set (enabled) {
super.isEnabled = enabled
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.textField.isEnabled = self.isEnabled
self.textField.textColor = self.isEnabled ? self.textColor.enabled : self.textColor.disabled
self.textField.isEnabled = enabled
self.textField.textColor = enabled ? self.textColor.enabled : self.textColor.disabled
}
}
}
/// The text of this textField.
public override var showError: Bool {
get { return super.showError }
set (error) {
textField.accessibilityValue = nil
super.showError = error
}
}
/// The text of this TextField.
public override var text: String? {
get { return textField.text }
set {
@ -74,6 +85,10 @@ import UIKit
set { textField.placeholder = newValue }
}
//--------------------------------------------------
// MARK: - Property Observers
//--------------------------------------------------
public var validationBlock: ((_ value: String?) -> Bool)? {
didSet { valueChanged() }
}
@ -122,21 +137,15 @@ import UIKit
// MARK: - Initializers
//--------------------------------------------------
public override init(frame: CGRect) {
@objc public override init(frame: CGRect) {
super.init(frame: frame)
}
public convenience init() {
@objc public convenience init() {
self.init(frame: .zero)
}
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
public init(bothDelegates: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
super.init(frame: .zero)
setBothTextDelegates(to: bothDelegates)
}
required public init?(coder: NSCoder) {
@objc required public init?(coder: NSCoder) {
super.init(coder: coder)
fatalError("TextEntryField does not support xib.")
}
@ -145,7 +154,7 @@ import UIKit
// MARK: - Lifecycle
//--------------------------------------------------
open override func setupFieldContainerContent(_ container: UIView) {
@objc open override func setupFieldContainerContent(_ container: UIView) {
MFStyler.styleTextField(textField)
container.addSubview(textField)
@ -162,42 +171,28 @@ import UIKit
accessibilityElements = [titleLabel, textField, feedbackLabel]
}
open override func updateView(_ size: CGFloat) {
@objc open override func updateView(_ size: CGFloat) {
super.updateView(size)
MFStyler.styleTextField(textField)
layoutIfNeeded()
}
deinit {
@objc deinit {
setBothTextDelegates(to: nil)
}
//--------------------------------------------------
// MARK: - Methods
//--------------------------------------------------
open func clearErrorState() {
textField.accessibilityValue = nil
feedback = nil
showError = false
}
public func setBothTextDelegates(to delegate: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
@objc public func setBothTextDelegates(to delegate: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
observingTextFieldDelegate = delegate
uiTextFieldDelegate = delegate
}
public func defaultValidationBlock() {
validationBlock = { enteredValue in
return (enteredValue?.count ?? 0) > 0
}
}
//--------------------------------------------------
// MARK: - Observing for Change (TextFieldDelegate)
//--------------------------------------------------
override open func resignFirstResponder() -> Bool {
@objc override open func resignFirstResponder() -> Bool {
textField.resignFirstResponder()
isSelected = false
@ -209,10 +204,14 @@ import UIKit
_ = self.resignFirstResponder()
}
//--------------------------------------------------
// MARK: - Observing for Change (TextFieldDelegate)
//--------------------------------------------------
public func defaultValidationBlock() {
validationBlock = { enteredValue in
return (enteredValue?.count ?? 0) > 0
}
}
/// Executes on UITextField.textDidChangeNotification
@objc func valueChanged() {
if !showError {
@ -225,19 +224,20 @@ import UIKit
isValid = validationBlock?(text) ?? true
if previousValidity && !isValid {
feedback = errorMessage
showError = true
observingTextFieldDelegate?.isInvalid?(textfield: self)
} else if !previousValidity && isValid {
clearErrorState()
showError = false
observingTextFieldDelegate?.isValid?(textfield: self)
}
}
/// Executes on UITextField.textDidEndEditingNotification
@objc func endInputing() {
if isValid {
clearErrorState()
showError = false
entryContainer.bottomBar?.backgroundColor = UIColor.black.cgColor
} else if let errMessage = errorMessage {
@ -245,6 +245,7 @@ import UIKit
}
}
/// Executes on UITextField.textDidBeginEditingNotification
@objc func startEditing() {
isSelected = true
@ -255,7 +256,7 @@ import UIKit
// MARK: - Molecular
extension TextEntryField {
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
@objc open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
guard let delegateObject = delegateObject,
@ -306,7 +307,7 @@ extension TextEntryField {
}
if let formValidationProtocol = delegateObject.formValidationProtocol {
observingTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol) as? ObservingTextFieldDelegate
observingTextFieldDelegate = FormValidator.getFormValidatorFor(delegate: formValidationProtocol)
}
uiTextFieldDelegate = delegateObject.uiTextFieldDelegate
@ -317,7 +318,7 @@ extension TextEntryField {
// MARK: - Accessibility
extension TextEntryField {
open override func pushAccessibilityNotification() {
@objc open override func pushAccessibilityNotification() {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
@ -326,7 +327,7 @@ extension TextEntryField {
}
}
open override func setAccessibilityString(_ accessibilityString: String?) {
@objc open override func setAccessibilityString(_ accessibilityString: String?) {
var accessibilityString = accessibilityString ?? ""

View File

@ -18,7 +18,7 @@
public var direction: Direction = .right
public var size: CaretSize?
public var enabledColor: UIColor = .black
public var disabledColor: UIColor = .mfSilver()
@ -48,17 +48,17 @@
case vertical
case horizontal
}
// Dimensions of container; provided by InVision.
// Dimensions of container; provided by InVision design.
func dimensions() -> CGSize {
switch self {
case .small(let o):
return o == .vertical ? CGSize(width: 6, height: 10) : CGSize(width: 10, height: 6)
case .medium(let o):
return o == .vertical ? CGSize(width: 9, height: 16) : CGSize(width: 16, height: 9)
case .large(let o):
return o == .vertical ? CGSize(width: 14, height: 24) : CGSize(width: 24, height: 14)
}
@ -114,7 +114,7 @@
caretPath.removeAllPoints()
caretPath.lineJoinStyle = .miter
caretPath.lineWidth = lineWidth
let inset = lineWidth / 2
let halfWidth = frame.size.width / 2
let halfHeight = frame.size.height / 2
@ -168,7 +168,7 @@
@objc public func setConstraints() {
guard let dimensions = size?.dimensions() else { return }
heightAnchor.constraint(equalToConstant: dimensions.height).isActive = true
widthAnchor.constraint(equalToConstant: dimensions.width).isActive = true
}
@ -191,16 +191,16 @@
strokeColor = UIColor.mfGet(forHex: strokeColorHex)
}
if let isHiddenValue = dictionary[KeyIsHidden] as? Bool {
isHidden = isHiddenValue
if let isHidden = dictionary[KeyIsHidden] as? Bool {
self.isHidden = isHidden
}
if let isOpaqueValue = dictionary[KeyIsOpaque] as? Bool {
isOpaque = isOpaqueValue
if let isOpaque = dictionary[KeyIsOpaque] as? Bool {
self.isOpaque = isOpaque
}
if let lineWidthValue = dictionary["lineWidth"] as? CGFloat {
lineWidth = lineWidthValue
if let lineWidth = dictionary["lineWidth"] as? CGFloat {
self.lineWidth = lineWidth
}
}

View File

@ -29,7 +29,6 @@ open class TextField: UITextField {
// MARK: - Delegate
//--------------------------------------------------
/// Holds a reference to the delegating class so this class can internally influence the TextField behavior as well.
private weak var proprietorTextDelegate: UITextFieldDelegate?

View File

@ -23,14 +23,23 @@ import UIKit
return layer
}()
/// Total control over bottom bar and the drawn borders.
public var disableBorders = false {
/// Total control overthe drawn top,bottom, left and right borders.
public var disableAllBorders = false {
didSet {
bottomBar?.isHidden = disableBorders
bottomBar?.isHidden = disableAllBorders
}
}
/// Determines if a border should be drawn.
private(set) var fieldState: FieldState = .original {
didSet (oldState) {
// Will not update if new state is the same as old.
if fieldState != oldState {
fieldState.setStateUI(for: self)
}
}
}
/// Determines if the top, left, and right borders should be drawn.
private var hideBorders = false
public var borderStrokeColor: UIColor = .mfSilver()
@ -40,27 +49,60 @@ import UIKit
// MARK: - Property Observers
//--------------------------------------------------
public var showError = false {
didSet {
showError ? errorUI() : originalUI()
private var _isEnabled: Bool = true
private var _showError: Bool = false
private var _isLocked: Bool = false
private var _isSelected: Bool = false
public var isEnabled: Bool {
get { return _isEnabled }
set (enabled) {
_isEnabled = enabled
_isLocked = false
_isSelected = false
_showError = false
fieldState = enabled ? .original : .disabled
}
}
public var isEnabled = true {
didSet {
isEnabled ? originalUI() : disabledUI()
public var showError: Bool {
get { return _showError }
set (error) {
_showError = error
_isEnabled = true
_isLocked = false
_isSelected = false
fieldState = error ? .error : .original
}
}
public var isLocked = false {
didSet {
isLocked ? lockedUI() : originalUI()
public var isLocked: Bool {
get { return _isLocked }
set (locked) {
_isLocked = locked
_isEnabled = true
_isSelected = false
_showError = false
fieldState = locked ? .locked : .original
}
}
public var isSelected = false {
didSet {
isSelected ? selectedUI() : originalUI()
public var isSelected: Bool {
get { return _isSelected }
set (selected) {
_isSelected = selected
_isLocked = false
_isEnabled = true
_showError = false
fieldState = selected ? .selected : .original
}
}
@ -93,7 +135,7 @@ import UIKit
borderPath.removeAllPoints()
if !disableBorders && !hideBorders {
if !disableAllBorders && !hideBorders {
// Brings the other half of the line inside the view to prevent cropping.
let origin = bounds.origin
let size = frame.size
@ -119,11 +161,22 @@ import UIKit
}
}
open override func reset() {
super.reset()
isEnabled = true
_isLocked = false
_isSelected = false
_showError = false
subviews.forEach { $0.removeFromSuperview() }
}
//--------------------------------------------------
// MARK: - Draw States
//--------------------------------------------------
public enum State {
public enum FieldState {
case original
case error
case selected
@ -162,8 +215,8 @@ import UIKit
open func errorUI() {
isUserInteractionEnabled = true
borderStrokeColor = .mfPumpkin()
hideBorders = false
borderStrokeColor = .mfPumpkin()
bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor
refreshUI(bottomBarSize: 4)
}
@ -171,8 +224,8 @@ import UIKit
open func selectedUI() {
isUserInteractionEnabled = true
borderStrokeColor = .black
hideBorders = false
borderStrokeColor = .black
bottomBar?.backgroundColor = UIColor.black.cgColor
refreshUI(bottomBarSize: 1)
}
@ -180,8 +233,8 @@ import UIKit
open func lockedUI() {
isUserInteractionEnabled = false
borderStrokeColor = .clear
hideBorders = true
borderStrokeColor = .clear
bottomBar?.backgroundColor = UIColor.clear.cgColor
refreshUI(bottomBarSize: 1)
}
@ -189,15 +242,15 @@ import UIKit
open func disabledUI() {
isUserInteractionEnabled = false
borderStrokeColor = .mfSilver()
hideBorders = false
borderStrokeColor = .mfSilver()
bottomBar?.backgroundColor = UIColor.mfSilver().cgColor
refreshUI(bottomBarSize: 1)
}
open func refreshUI(bottomBarSize: CGFloat? = nil) {
if !disableBorders {
if !disableAllBorders {
let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1)
bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size)
@ -217,8 +270,8 @@ import UIKit
guard let dictionary = json, !dictionary.isEmpty else { return }
if let disableBorders = dictionary["disableBorders"] as? Bool {
self.disableBorders = disableBorders
if let disableAllBorders = dictionary["disableAllBorders"] as? Bool {
self.disableAllBorders = disableAllBorders
}
}
}

View File

@ -38,8 +38,9 @@
@"caretButton": CaretButton.class,
@"textField": TextEntryField.class,
@"digitEntryField": DigitEntryField.class,
@"itemDropdownEntryField": ItemDropdownEntryField.class,
@"dateDropdownEntryField": DateDropdownEntryField.class,
@"mdnEntryField" : MdnEntryField.class,
@"dropdownEntryField" : DropdownEntryField.class,
@"checkbox" : Checkbox.class,
@"checkboxWithLabel" : CheckboxWithLabelView.class,
@"cornerLabels" : CornerLabels.class,