latestest and greatest.

This commit is contained in:
Kevin G Christiano 2019-11-20 09:12:31 -05:00
parent 702dc10db2
commit 26c73070d9
7 changed files with 269 additions and 102 deletions

View File

@ -9,11 +9,12 @@
import UIKit import UIKit
@objc protocol DigitBoxDelegate: NSObjectProtocol { @objc protocol DigitBoxDelegate: NSObjectProtocol {
@objc optional func textFieldDidDelete(_ textField: UITextField?) @objc optional func digitFieldDidDelete(_ textField: UITextField?)
@objc optional func textFieldDidChange(_ textField: UITextField)
} }
@objcMembers open class DigitBox: FormView, UITextFieldDelegate { @objcMembers open class DigitBox: FormFieldContainer, UITextFieldDelegate {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Outlets // MARK: - Outlets
//-------------------------------------------------- //--------------------------------------------------
@ -46,8 +47,8 @@ import UIKit
self.borderStrokeColor = self.showError ? .mfPumpkin() : .mfSilver() self.borderStrokeColor = self.showError ? .mfPumpkin() : .mfSilver()
let barHeight: CGFloat = self.showError ? 4 : 1 let barHeight: CGFloat = self.showError ? 4 : 1
self.bottomBar.frame = CGRect(x: 0, y: self.bounds.height - barHeight, width: self.bounds.width, height: barHeight) self.bottomBar?.frame = CGRect(x: 0, y: self.bounds.height - barHeight, width: self.bounds.width, height: barHeight)
self.bottomBar.backgroundColor = self.showError ? UIColor.mfPumpkin().cgColor : UIColor.black.cgColor self.bottomBar?.backgroundColor = self.showError ? UIColor.mfPumpkin().cgColor : UIColor.black.cgColor
self.setNeedsDisplay() self.setNeedsDisplay()
self.layoutIfNeeded() self.layoutIfNeeded()
@ -59,7 +60,7 @@ import UIKit
// MARK: - Delegate // MARK: - Delegate
//-------------------------------------------------- //--------------------------------------------------
weak var textBoxDelegate: DigitBoxDelegate? weak var digitBoxDelegate: DigitBoxDelegate?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Constraints // MARK: - Constraints
@ -102,39 +103,54 @@ import UIKit
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
digitField.heightAnchor.constraint(equalToConstant: 24), digitField.heightAnchor.constraint(equalToConstant: 24),
digitField.topAnchor.constraint(equalTo: topAnchor, constant: 12), digitField.topAnchor.constraint(greaterThanOrEqualTo: topAnchor),
digitField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12), digitField.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
bottomAnchor.constraint(equalTo: digitField.bottomAnchor, constant: 12), bottomAnchor.constraint(greaterThanOrEqualTo: digitField.bottomAnchor),
trailingAnchor.constraint(equalTo: digitField.trailingAnchor, constant: 12)]) trailingAnchor.constraint(greaterThanOrEqualTo: digitField.trailingAnchor),
digitField.centerYAnchor.constraint(equalTo: centerYAnchor),
digitField.centerXAnchor.constraint(equalTo: centerXAnchor)])
widthConstraint = widthAnchor.constraint(equalToConstant: 39) widthConstraint = widthAnchor.constraint(equalToConstant: 39)
widthConstraint?.isActive = true widthConstraint?.isActive = true
heightConstraint = heightAnchor.constraint(equalToConstant: 44) heightConstraint = heightAnchor.constraint(equalToConstant: 44)
heightConstraint?.isActive = true heightConstraint?.isActive = true
layer.addSublayer(bottomBar) if let bottomBar = bottomBar {
layer.addSublayer(bottomBar)
}
updateView(MVMCoreUISplitViewController.getDetailViewWidth()) updateView(MVMCoreUISplitViewController.getDetailViewWidth())
digitField.addTarget(self, action:#selector(textfieldChanged) , for: .valueChanged)
}
func textfieldChanged() {
} }
open override func layoutSubviews() { open override func layoutSubviews() {
super.layoutSubviews() super.layoutSubviews()
let barHeight: CGFloat = showError ? 4 : 1 let barHeight: CGFloat = showError ? 4 : 1
bottomBar.frame = CGRect(x: 0, y: bounds.height - barHeight, width: bounds.width, height: barHeight) bottomBar?.frame = CGRect(x: 0, y: bounds.height - barHeight, width: bounds.width, height: barHeight)
}
open override func reset() {
super.reset()
digitField.text = nil
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
// public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
//
// if string.isBackspace { if string.isBackspace {
// textBoxDelegate?.textFieldDidDelete?(self.digitField) digitBoxDelegate?.digitFieldDidDelete?(self.digitField)
// } }
//
// return true return true
// } }
public override func updateView(_ size: CGFloat) { public override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
@ -175,10 +191,10 @@ import UIKit
// TODO: Move if working properly. // TODO: Move if working properly.
//extension String { extension String {
//
// var isBackspace: Bool { var isBackspace: Bool {
// let char = self.cString(using: String.Encoding.utf8)! let char = self.cString(using: String.Encoding.utf8)!
// return strcmp(char, "\\b") == -92 return strcmp(char, "\\b") == -92
// } }
//} }

View File

@ -19,7 +19,8 @@ import UIKit
private(set) var numberOfDigits = 4 private(set) var numberOfDigits = 4
public var switchFieldsAutomatically = false public var switchFieldsAutomatically = false
public var digitFields: [DigitBox] = [] public var digitBoxes: [DigitBox] = []
var selectedDigitField: DigitBox?
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Property Observers // MARK: - Property Observers
@ -29,7 +30,7 @@ import UIKit
didSet { didSet {
titleLabel.textColor = self.isEnabled ? .mfBattleshipGrey() : .mfSilver() titleLabel.textColor = self.isEnabled ? .mfBattleshipGrey() : .mfSilver()
digitFields.forEach { digitBoxes.forEach {
$0.isEnabled = self.isEnabled $0.isEnabled = self.isEnabled
$0.isUserInteractionEnabled = isEnabled $0.isUserInteractionEnabled = isEnabled
$0.digitField.isEnabled = isEnabled $0.digitField.isEnabled = isEnabled
@ -38,17 +39,29 @@ import UIKit
} }
} }
public override var showError: Bool {
didSet {
digitBoxes.forEach { $0.showError = self.showError }
}
}
public override var isLocked: Bool {
didSet {
digitBoxes.forEach { $0.isLocked = self.isLocked }
}
}
public override var placeholder: String? { public override var placeholder: String? {
get { get {
var string = "" var string = ""
digitFields.forEach { string += $0.digitField.attributedPlaceholder?.string ?? "" } digitBoxes.forEach { string += $0.digitField.attributedPlaceholder?.string ?? "" }
return !string.isEmpty ? string : nil return !string.isEmpty ? string : nil
} }
set { set {
guard let newValue = newValue else { return } guard let newValue = newValue else { return }
for (index, field) in digitFields.enumerated() { for (index, field) in digitBoxes.enumerated() {
if index < newValue.count { if index < newValue.count {
let indexChar = newValue.index(newValue.startIndex, offsetBy: index) let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
field.digitField.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [ field.digitField.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [
@ -76,14 +89,14 @@ import UIKit
public override var text: String? { public override var text: String? {
get { get {
var string = "" var string = ""
digitFields.forEach { string += $0.digitField.text ?? "" } digitBoxes.forEach { string += $0.digitField.text ?? "" }
return string return string
} }
set { set {
guard let newValue = newValue else { return } guard let newValue = newValue else { return }
for (index, field) in digitFields.enumerated() { for (index, field) in digitBoxes.enumerated() {
if index < newValue.count { if index < newValue.count {
let indexChar = newValue.index(newValue.startIndex, offsetBy: index) let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
field.digitField.text = String(newValue[indexChar]) field.digitField.text = String(newValue[indexChar])
@ -94,6 +107,22 @@ import UIKit
} }
} }
/// 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: - 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: - Initializers // MARK: - Initializers
//-------------------------------------------------- //--------------------------------------------------
@ -129,9 +158,7 @@ import UIKit
alignCenterHorizontal() alignCenterHorizontal()
isAccessibilityElement = false isAccessibilityElement = false
entryContainer.hideBorders = true entryContainer.disableBorders = true
entryContainer.bottomBar.backgroundColor = UIColor.clear.cgColor
entryContainer.bottomBar.frame = CGRect(x: 0, y: entryContainer.bounds.height, width: 0, height: 0)
assembleDigitFieldsView(size: MVMCoreUISplitViewController.getDetailViewWidth()) assembleDigitFieldsView(size: MVMCoreUISplitViewController.getDetailViewWidth())
} }
@ -139,8 +166,9 @@ import UIKit
let digitBox = DigitBox() let digitBox = DigitBox()
digitBox.isAccessibilityElement = true digitBox.isAccessibilityElement = true
MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: self)
digitBox.digitField.delegate = self digitBox.digitField.delegate = self
digitBox.textBoxDelegate = self digitBox.digitBoxDelegate = self
return digitBox return digitBox
} }
@ -149,17 +177,17 @@ import UIKit
var accessibleElements: [Any] = [titleLabel] var accessibleElements: [Any] = [titleLabel]
if numberOfDigits > 0 { if numberOfDigits > 0 {
var digitFields = [DigitBox]() var digitBoxes = [DigitBox]()
for _ in 0..<numberOfDigits { for _ in 0..<numberOfDigits {
digitFields.append(createDigitField()) digitBoxes.append(createDigitField())
} }
self.digitFields = digitFields self.digitBoxes = digitBoxes
guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize() else { return } guard let space = MFSizeObject(standardSize: 5, smalliPhoneSize: 3)?.getValueBasedOnScreenSize() else { return }
var prevBox: DigitBox? var prevBox: DigitBox?
for (index, box) in digitFields.enumerated() { for (index, box) in digitBoxes.enumerated() {
accessibleElements.append(box) accessibleElements.append(box)
entryContainer.addSubview(box) entryContainer.addSubview(box)
@ -169,7 +197,7 @@ import UIKit
if index == 0 { if index == 0 {
box.leadingAnchor.constraint(equalTo: entryContainer.leadingAnchor).isActive = true box.leadingAnchor.constraint(equalTo: entryContainer.leadingAnchor).isActive = true
} else if index == digitFields.count - 1 { } else if index == digitBoxes.count - 1 {
box.leadingAnchor.constraint(equalTo: prevBox!.trailingAnchor, constant: space).isActive = true box.leadingAnchor.constraint(equalTo: prevBox!.trailingAnchor, constant: space).isActive = true
entryContainer.trailingAnchor.constraint(greaterThanOrEqualTo: box.trailingAnchor).isActive = true entryContainer.trailingAnchor.constraint(greaterThanOrEqualTo: box.trailingAnchor).isActive = true
@ -180,7 +208,7 @@ import UIKit
prevBox = box prevBox = box
} }
} else { } else {
digitFields = [] digitBoxes = []
} }
accessibilityElements = accessibleElements + [feedbackLabel] accessibilityElements = accessibleElements + [feedbackLabel]
@ -193,19 +221,26 @@ import UIKit
open override func updateView(_ size: CGFloat) { open override func updateView(_ size: CGFloat) {
super.updateView(size) super.updateView(size)
entryContainer.hideBorders = true entryContainer.disableBorders = true
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
if !self.digitFields.isEmpty { if !self.digitBoxes.isEmpty {
self.digitFields.forEach { $0.updateView(size) } self.digitBoxes.forEach { $0.updateView(size) }
self.layoutIfNeeded() self.layoutIfNeeded()
} }
} }
} }
open override func reset() {
super.reset()
entryContainer.disableBorders = false
digitBoxes.forEach { $0.reset() }
}
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Methods // MARK: - Methods
//-------------------------------------------------- //--------------------------------------------------
@ -215,11 +250,11 @@ import UIKit
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
for (index, field) in self.digitFields.enumerated() { for (index, field) in self.digitBoxes.enumerated() {
field.digitField.isSecureTextEntry = secureEntry field.digitField.isSecureTextEntry = secureEntry
// Accessibility - 33704 fix voice over will read what pin user is filling // Accessibility - 33704 fix voice over will read what pin user is filling
field.accessibilityLabel = String(format: "PIN %lu of %lu", UInt(index) + 1, UInt(self.digitFields.count)) field.accessibilityLabel = String(format: "PIN %lu of %lu", UInt(index) + 1, UInt(self.digitBoxes.count))
} }
} }
} }
@ -229,21 +264,21 @@ import UIKit
validationBlock = { enteredValue in validationBlock = { enteredValue in
guard let enteredValue = enteredValue else { return false } guard let enteredValue = enteredValue else { return false }
return enteredValue.count > 0 && enteredValue.count == self.digitFields.count return enteredValue.count > 0 && enteredValue.count == self.digitBoxes.count
} }
} }
public func selectPreviousDigitField(_ currentTextField: UITextField?, clear: Bool) { public func selectPreviousDigitField(_ currentTextField: UITextField?, clear: Bool) {
var selectNextField = false var selectPreviousField = false
for field in digitFields { for field in digitBoxes.reversed() {
if field.digitField == currentTextField { if field.isSelected {
selectNextField = true selectPreviousField = true
field.isSelected = false field.isSelected = false
} else if selectNextField { } else if selectPreviousField {
if !clear { if !clear {
switchFieldsAutomatically = true switchFieldsAutomatically = true
} }
@ -252,6 +287,7 @@ import UIKit
switchFieldsAutomatically = false switchFieldsAutomatically = false
UIAccessibility.post(notification: .layoutChanged, argument: field.digitField) UIAccessibility.post(notification: .layoutChanged, argument: field.digitField)
return
} }
} }
} }
@ -260,42 +296,77 @@ import UIKit
var selectNextField = false var selectNextField = false
for field in digitFields { for (index, field) in digitBoxes.enumerated() {
if field.digitField == currentTextField {
selectNextField = true if field.isSelected {
field.isSelected = false if index == digitBoxes.count - 1 {
return
} else {
selectNextField = true
field.isSelected = false
}
} else if selectNextField { } else if selectNextField {
if !clear { if !clear {
switchFieldsAutomatically = true switchFieldsAutomatically = true
} }
field.isSelected = true field.isSelected = true
field.becomeFirstResponder() field.digitField.becomeFirstResponder()
switchFieldsAutomatically = false switchFieldsAutomatically = false
UIAccessibility.post(notification: .layoutChanged, argument: field.digitField) UIAccessibility.post(notification: .layoutChanged, argument: field.digitField)
return
} }
} }
} }
open class func getEnabledDigitFields(_ textFieldToDetermine: [TextEntryField]) -> [AnyHashable]? { @objc override func startEditing() {
return textFieldToDetermine.filter { $0.isEnabled } selectedDigitField?.isSelected = true
selectedDigitField?.digitField.becomeFirstResponder()
}
override open func resignFirstResponder() -> Bool {
selectedDigitField?.digitField.resignFirstResponder()
selectedDigitField?.isSelected = false
return true
}
@objc override func dismissFieldInput(_ sender: Any?) {
for box in digitBoxes {
if box.isSelected {
box.digitField.resignFirstResponder()
box.isSelected = false
}
}
}
open class func getEnabledDigitFields(_ textFieldToDetermine: [DigitBox]) -> [TextField]? {
return textFieldToDetermine.filter { $0.isEnabled }.compactMap { $0.digitField }
} }
} }
// MARK: - TextField Delegate // MARK: - TextField Delegate
extension DigitEntryField { extension DigitEntryField {
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool { @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder() textField.resignFirstResponder()
return uiTextFieldDelegate?.textFieldShouldReturn?(textField) ?? true return proprietorTextDelegate?.textFieldShouldReturn?(textField) ?? true
} }
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if string.isBackspace {
selectPreviousDigitField(textField, clear: true)
return true
}
if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) { if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) {
return false return false
} }
@ -329,7 +400,7 @@ extension DigitEntryField {
return false return false
} }
func textFieldDidDelete(_ textField: UITextField?) { func digitFieldDidDelete(_ textField: UITextField?) {
// Empty cell, go back to previous cell and clear. // Empty cell, go back to previous cell and clear.
selectPreviousDigitField(textField, clear: true) selectPreviousDigitField(textField, clear: true)
@ -337,34 +408,46 @@ extension DigitEntryField {
@objc public func textFieldDidBeginEditing(_ textField: UITextField) { @objc public func textFieldDidBeginEditing(_ textField: UITextField) {
for digitBox in digitBoxes {
if digitBox.isSelected {
digitBox.isSelected = false
}
if digitBox.digitField == textField {
selectedDigitField = digitBox
digitBox.isSelected = true
}
}
if !switchFieldsAutomatically { if !switchFieldsAutomatically {
textField.text = "" textField.text = ""
valueChanged() valueChanged()
} }
uiTextFieldDelegate?.textFieldDidBeginEditing?(textField) proprietorTextDelegate?.textFieldDidBeginEditing?(textField)
} }
@objc public func textFieldDidEndEditing(_ textField: UITextField) { @objc public func textFieldDidEndEditing(_ textField: UITextField) {
uiTextFieldDelegate?.textFieldDidEndEditing?(textField) proprietorTextDelegate?.textFieldDidEndEditing?(textField)
} }
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool { @objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
selectPreviousDigitField(textField, clear: false) selectPreviousDigitField(textField, clear: false)
return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true return proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
} }
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
} }
@objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { @objc public func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
return uiTextFieldDelegate?.textFieldShouldEndEditing?(textField) ?? true return proprietorTextDelegate?.textFieldShouldEndEditing?(textField) ?? true
} }
} }
@ -384,7 +467,7 @@ extension DigitEntryField {
assembleDigitFieldsView(size: MVMCoreUIUtility.getWidth()) assembleDigitFieldsView(size: MVMCoreUIUtility.getWidth())
if !dictionary.isEmpty{ if !dictionary.isEmpty{
for digitBox in digitFields { for digitBox in digitBoxes {
MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate) MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate)
} }
} }

View File

@ -27,8 +27,8 @@ import UIKit
return label return label
}() }()
public private(set) var entryContainer: FormView = { public private(set) var entryContainer: FormFieldContainer = {
let view = FormView() let view = FormFieldContainer()
view.isAccessibilityElement = false view.isAccessibilityElement = false
return view return view
}() }()
@ -63,6 +63,11 @@ import UIKit
/// Toggles error or original UI. /// Toggles error or original UI.
public var showError = false { public var showError = false {
willSet {
isLocked = false
isSelected = false
isEnabled = false
}
didSet { didSet {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -75,6 +80,11 @@ import UIKit
/// Toggles enabled (original) or disabled UI. /// Toggles enabled (original) or disabled UI.
public var isEnabled = true { public var isEnabled = true {
willSet {
isLocked = false
isSelected = false
showError = false
}
didSet { didSet {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -89,6 +99,11 @@ import UIKit
/// Toggles original or locked UI. /// Toggles original or locked UI.
public var isLocked = false { public var isLocked = false {
willSet {
isEnabled = true
isSelected = false
showError = false
}
didSet { didSet {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -101,6 +116,11 @@ import UIKit
/// Toggles selected or original (unselected) UI. /// Toggles selected or original (unselected) UI.
public var isSelected = false { public var isSelected = false {
willSet {
isLocked = false
isEnabled = true
showError = false
}
didSet { didSet {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }

View File

@ -61,10 +61,10 @@ import MVMCore
} }
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates. /// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
public override init(bothDelegates: (UITextFieldDelegate & ObservingTextFieldDelegate)?) { public override init(bothDelegates: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
super.init(frame: .zero) super.init(frame: .zero)
setBothTextDelegates(to: bothDelegates) setBothTextDelegates(to: bothDelegates)
} }
required public init?(coder: NSCoder) { required public init?(coder: NSCoder) {
super.init(coder: coder) super.init(coder: coder)

View File

@ -238,7 +238,7 @@ import UIKit
if isValid { if isValid {
clearErrorState() clearErrorState()
entryContainer.bottomBar.backgroundColor = UIColor.black.cgColor entryContainer.bottomBar?.backgroundColor = UIColor.black.cgColor
} else if let errMessage = errorMessage { } else if let errMessage = errorMessage {
feedback = errMessage feedback = errMessage

View File

@ -8,6 +8,10 @@
import UIKit import UIKit
protocol TextFieldDelegate {
func textFieldDidDelete()
}
open class TextField: UITextField { open class TextField: UITextField {
//-------------------------------------------------- //--------------------------------------------------
@ -21,6 +25,14 @@ open class TextField: UITextField {
/// Set to true to hide the blinking textField cursor. /// Set to true to hide the blinking textField cursor.
public var hideBlinkingCaret = false public var hideBlinkingCaret = false
//--------------------------------------------------
// 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: - Initialization // MARK: - Initialization
//-------------------------------------------------- //--------------------------------------------------
@ -55,6 +67,11 @@ open class TextField: UITextField {
return super.caretRect(for: position) return super.caretRect(for: position)
} }
open override func deleteBackward() {
super.deleteBackward()
// proprietorTextDelegate?.textFieldDidDelete()
}
} }
/// MARK:- MVMCoreViewProtocol /// MARK:- MVMCoreViewProtocol

View File

@ -9,13 +9,13 @@
import UIKit import UIKit
@objcMembers open class FormView: View { @objcMembers open class FormFieldContainer: View {
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Drawing Properties // MARK: - Drawing Properties
//-------------------------------------------------- //--------------------------------------------------
/// The bottom border line. /// The bottom border line. Height is dynamic based on scenario.
public var bottomBar: CAShapeLayer = { public var bottomBar: CAShapeLayer? = {
let layer = CAShapeLayer() let layer = CAShapeLayer()
layer.backgroundColor = UIColor.black.cgColor layer.backgroundColor = UIColor.black.cgColor
layer.drawsAsynchronously = true layer.drawsAsynchronously = true
@ -23,8 +23,15 @@ import UIKit
return layer return layer
}() }()
/// Total control over bottom bar and the drawn borders.
public var disableBorders = false {
didSet {
bottomBar?.isHidden = disableBorders
}
}
/// Determines if a border should be drawn. /// Determines if a border should be drawn.
public var hideBorders = false private var hideBorders = false
public var borderStrokeColor: UIColor = .mfSilver() public var borderStrokeColor: UIColor = .mfSilver()
private var borderPath: UIBezierPath = UIBezierPath() private var borderPath: UIBezierPath = UIBezierPath()
@ -35,17 +42,13 @@ import UIKit
public var showError = false { public var showError = false {
didSet { didSet {
if !hideBorders { showError ? errorUI() : originalUI()
showError ? errorUI() : originalUI()
}
} }
} }
public var isEnabled = true { public var isEnabled = true {
didSet { didSet {
if !hideBorders { isEnabled ? originalUI() : disabledUI()
isEnabled ? originalUI() : disabledUI()
}
} }
} }
@ -57,9 +60,7 @@ import UIKit
public var isSelected = false { public var isSelected = false {
didSet { didSet {
if !hideBorders { isSelected ? selectedUI() : originalUI()
isSelected ? selectedUI() : originalUI()
}
} }
} }
@ -92,7 +93,7 @@ import UIKit
borderPath.removeAllPoints() borderPath.removeAllPoints()
if !hideBorders { if !disableBorders && !hideBorders {
// Brings the other half of the line inside the view to prevent cropping. // Brings the other half of the line inside the view to prevent cropping.
let origin = bounds.origin let origin = bounds.origin
let size = frame.size let size = frame.size
@ -113,46 +114,75 @@ import UIKit
super.setupView() super.setupView()
isOpaque = false isOpaque = false
layer.addSublayer(bottomBar) if let bottomBar = bottomBar {
layer.addSublayer(bottomBar)
}
} }
//-------------------------------------------------- //--------------------------------------------------
// MARK: - Draw States // MARK: - Draw States
//-------------------------------------------------- //--------------------------------------------------
public enum State {
case original
case error
case selected
case locked
case disabled
public func setStateUI(for formField: FormFieldContainer) {
switch self {
case .original:
formField.originalUI()
case .error:
formField.errorUI()
case .selected:
formField.selectedUI()
case .locked:
formField.lockedUI()
case .disabled:
formField.disabledUI()
}
}
}
open func originalUI() { open func originalUI() {
isUserInteractionEnabled = true isUserInteractionEnabled = true
hideBorders = false hideBorders = false
borderStrokeColor = .mfSilver() borderStrokeColor = .mfSilver()
bottomBar.backgroundColor = UIColor.black.cgColor bottomBar?.backgroundColor = UIColor.black.cgColor
refreshUI(bottomBarSize: 1) refreshUI(bottomBarSize: 1)
} }
open func errorUI() { open func errorUI() {
isUserInteractionEnabled = true isUserInteractionEnabled = true
hideBorders = false
borderStrokeColor = .mfPumpkin() borderStrokeColor = .mfPumpkin()
bottomBar.backgroundColor = UIColor.mfPumpkin().cgColor hideBorders = false
bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor
refreshUI(bottomBarSize: 4) refreshUI(bottomBarSize: 4)
} }
open func selectedUI() { open func selectedUI() {
isUserInteractionEnabled = true isUserInteractionEnabled = true
hideBorders = false
borderStrokeColor = .black borderStrokeColor = .black
bottomBar.backgroundColor = UIColor.black.cgColor hideBorders = false
bottomBar?.backgroundColor = UIColor.black.cgColor
refreshUI(bottomBarSize: 1) refreshUI(bottomBarSize: 1)
} }
open func lockedUI() { open func lockedUI() {
isUserInteractionEnabled = false isUserInteractionEnabled = false
hideBorders = true
borderStrokeColor = .clear borderStrokeColor = .clear
bottomBar.backgroundColor = UIColor.clear.cgColor hideBorders = true
bottomBar?.backgroundColor = UIColor.clear.cgColor
refreshUI(bottomBarSize: 1) refreshUI(bottomBarSize: 1)
} }
@ -160,15 +190,16 @@ import UIKit
isUserInteractionEnabled = false isUserInteractionEnabled = false
borderStrokeColor = .mfSilver() borderStrokeColor = .mfSilver()
bottomBar.backgroundColor = UIColor.mfSilver().cgColor hideBorders = false
bottomBar?.backgroundColor = UIColor.mfSilver().cgColor
refreshUI(bottomBarSize: 1) refreshUI(bottomBarSize: 1)
} }
open func refreshUI(bottomBarSize: CGFloat? = nil) { open func refreshUI(bottomBarSize: CGFloat? = nil) {
if !hideBorders { if !disableBorders {
let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1) let size: CGFloat = bottomBarSize ?? (showError ? 4 : 1)
bottomBar.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size) bottomBar?.frame = CGRect(x: 0, y: bounds.height - size, width: bounds.width, height: size)
delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self) delegateObject?.moleculeDelegate?.moleculeLayoutUpdated?(self)
setNeedsDisplay() setNeedsDisplay()
@ -186,8 +217,8 @@ import UIKit
guard let dictionary = json, !dictionary.isEmpty else { return } guard let dictionary = json, !dictionary.isEmpty else { return }
if let hideBorders = dictionary["hideBorders"] as? Bool { if let disableBorders = dictionary["disableBorders"] as? Bool {
self.hideBorders = hideBorders self.disableBorders = disableBorders
} }
} }
} }