latestest and greatest.
This commit is contained in:
parent
702dc10db2
commit
26c73070d9
@ -9,11 +9,12 @@
|
||||
import UIKit
|
||||
|
||||
@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
|
||||
//--------------------------------------------------
|
||||
@ -46,8 +47,8 @@ import UIKit
|
||||
self.borderStrokeColor = self.showError ? .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)
|
||||
self.bottomBar.backgroundColor = self.showError ? UIColor.mfPumpkin().cgColor : UIColor.black.cgColor
|
||||
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.setNeedsDisplay()
|
||||
self.layoutIfNeeded()
|
||||
@ -59,7 +60,7 @@ import UIKit
|
||||
// MARK: - Delegate
|
||||
//--------------------------------------------------
|
||||
|
||||
weak var textBoxDelegate: DigitBoxDelegate?
|
||||
weak var digitBoxDelegate: DigitBoxDelegate?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
@ -102,39 +103,54 @@ import UIKit
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
digitField.heightAnchor.constraint(equalToConstant: 24),
|
||||
digitField.topAnchor.constraint(equalTo: topAnchor, constant: 12),
|
||||
digitField.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 12),
|
||||
bottomAnchor.constraint(equalTo: digitField.bottomAnchor, constant: 12),
|
||||
trailingAnchor.constraint(equalTo: digitField.trailingAnchor, constant: 12)])
|
||||
digitField.topAnchor.constraint(greaterThanOrEqualTo: topAnchor),
|
||||
digitField.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor),
|
||||
bottomAnchor.constraint(greaterThanOrEqualTo: digitField.bottomAnchor),
|
||||
trailingAnchor.constraint(greaterThanOrEqualTo: digitField.trailingAnchor),
|
||||
digitField.centerYAnchor.constraint(equalTo: centerYAnchor),
|
||||
digitField.centerXAnchor.constraint(equalTo: centerXAnchor)])
|
||||
|
||||
widthConstraint = widthAnchor.constraint(equalToConstant: 39)
|
||||
widthConstraint?.isActive = true
|
||||
heightConstraint = heightAnchor.constraint(equalToConstant: 44)
|
||||
heightConstraint?.isActive = true
|
||||
|
||||
layer.addSublayer(bottomBar)
|
||||
if let bottomBar = bottomBar {
|
||||
layer.addSublayer(bottomBar)
|
||||
}
|
||||
updateView(MVMCoreUISplitViewController.getDetailViewWidth())
|
||||
digitField.addTarget(self, action:#selector(textfieldChanged) , for: .valueChanged)
|
||||
}
|
||||
|
||||
func textfieldChanged() {
|
||||
|
||||
}
|
||||
|
||||
open override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
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
|
||||
//--------------------------------------------------
|
||||
|
||||
// public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
//
|
||||
// if string.isBackspace {
|
||||
// textBoxDelegate?.textFieldDidDelete?(self.digitField)
|
||||
// }
|
||||
//
|
||||
// return true
|
||||
// }
|
||||
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)
|
||||
@ -175,10 +191,10 @@ 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
|
||||
// }
|
||||
//}
|
||||
extension String {
|
||||
|
||||
var isBackspace: Bool {
|
||||
let char = self.cString(using: String.Encoding.utf8)!
|
||||
return strcmp(char, "\\b") == -92
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,8 @@ import UIKit
|
||||
private(set) var numberOfDigits = 4
|
||||
public var switchFieldsAutomatically = false
|
||||
|
||||
public var digitFields: [DigitBox] = []
|
||||
public var digitBoxes: [DigitBox] = []
|
||||
var selectedDigitField: DigitBox?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Property Observers
|
||||
@ -29,7 +30,7 @@ import UIKit
|
||||
didSet {
|
||||
titleLabel.textColor = self.isEnabled ? .mfBattleshipGrey() : .mfSilver()
|
||||
|
||||
digitFields.forEach {
|
||||
digitBoxes.forEach {
|
||||
$0.isEnabled = self.isEnabled
|
||||
$0.isUserInteractionEnabled = 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? {
|
||||
get {
|
||||
var string = ""
|
||||
digitFields.forEach { string += $0.digitField.attributedPlaceholder?.string ?? "" }
|
||||
digitBoxes.forEach { string += $0.digitField.attributedPlaceholder?.string ?? "" }
|
||||
|
||||
return !string.isEmpty ? string : nil
|
||||
}
|
||||
set {
|
||||
guard let newValue = newValue else { return }
|
||||
|
||||
for (index, field) in digitFields.enumerated() {
|
||||
for (index, field) in digitBoxes.enumerated() {
|
||||
if index < newValue.count {
|
||||
let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
|
||||
field.digitField.attributedPlaceholder = NSAttributedString(string: String(newValue[indexChar]), attributes: [
|
||||
@ -76,14 +89,14 @@ import UIKit
|
||||
public override var text: String? {
|
||||
get {
|
||||
var string = ""
|
||||
digitFields.forEach { string += $0.digitField.text ?? "" }
|
||||
digitBoxes.forEach { string += $0.digitField.text ?? "" }
|
||||
|
||||
return string
|
||||
}
|
||||
set {
|
||||
guard let newValue = newValue else { return }
|
||||
|
||||
for (index, field) in digitFields.enumerated() {
|
||||
for (index, field) in digitBoxes.enumerated() {
|
||||
if index < newValue.count {
|
||||
let indexChar = newValue.index(newValue.startIndex, offsetBy: index)
|
||||
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
|
||||
//--------------------------------------------------
|
||||
@ -129,9 +158,7 @@ import UIKit
|
||||
|
||||
alignCenterHorizontal()
|
||||
isAccessibilityElement = false
|
||||
entryContainer.hideBorders = true
|
||||
entryContainer.bottomBar.backgroundColor = UIColor.clear.cgColor
|
||||
entryContainer.bottomBar.frame = CGRect(x: 0, y: entryContainer.bounds.height, width: 0, height: 0)
|
||||
entryContainer.disableBorders = true
|
||||
assembleDigitFieldsView(size: MVMCoreUISplitViewController.getDetailViewWidth())
|
||||
}
|
||||
|
||||
@ -139,8 +166,9 @@ import UIKit
|
||||
|
||||
let digitBox = DigitBox()
|
||||
digitBox.isAccessibilityElement = true
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: self)
|
||||
digitBox.digitField.delegate = self
|
||||
digitBox.textBoxDelegate = self
|
||||
digitBox.digitBoxDelegate = self
|
||||
return digitBox
|
||||
}
|
||||
|
||||
@ -149,17 +177,17 @@ import UIKit
|
||||
var accessibleElements: [Any] = [titleLabel]
|
||||
|
||||
if numberOfDigits > 0 {
|
||||
var digitFields = [DigitBox]()
|
||||
var digitBoxes = [DigitBox]()
|
||||
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 }
|
||||
|
||||
var prevBox: DigitBox?
|
||||
|
||||
for (index, box) in digitFields.enumerated() {
|
||||
for (index, box) in digitBoxes.enumerated() {
|
||||
accessibleElements.append(box)
|
||||
entryContainer.addSubview(box)
|
||||
|
||||
@ -169,7 +197,7 @@ import UIKit
|
||||
if index == 0 {
|
||||
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
|
||||
entryContainer.trailingAnchor.constraint(greaterThanOrEqualTo: box.trailingAnchor).isActive = true
|
||||
|
||||
@ -180,7 +208,7 @@ import UIKit
|
||||
prevBox = box
|
||||
}
|
||||
} else {
|
||||
digitFields = []
|
||||
digitBoxes = []
|
||||
}
|
||||
|
||||
accessibilityElements = accessibleElements + [feedbackLabel]
|
||||
@ -193,19 +221,26 @@ import UIKit
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
|
||||
entryContainer.hideBorders = true
|
||||
entryContainer.disableBorders = true
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
|
||||
entryContainer.disableBorders = false
|
||||
digitBoxes.forEach { $0.reset() }
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
@ -215,11 +250,11 @@ import UIKit
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
for (index, field) in self.digitFields.enumerated() {
|
||||
for (index, field) in self.digitBoxes.enumerated() {
|
||||
field.digitField.isSecureTextEntry = secureEntry
|
||||
|
||||
// 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
|
||||
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) {
|
||||
|
||||
var selectNextField = false
|
||||
var selectPreviousField = false
|
||||
|
||||
for field in digitFields {
|
||||
for field in digitBoxes.reversed() {
|
||||
|
||||
if field.digitField == currentTextField {
|
||||
selectNextField = true
|
||||
if field.isSelected {
|
||||
selectPreviousField = true
|
||||
field.isSelected = false
|
||||
|
||||
} else if selectNextField {
|
||||
} else if selectPreviousField {
|
||||
if !clear {
|
||||
switchFieldsAutomatically = true
|
||||
}
|
||||
@ -252,6 +287,7 @@ import UIKit
|
||||
switchFieldsAutomatically = false
|
||||
|
||||
UIAccessibility.post(notification: .layoutChanged, argument: field.digitField)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -260,42 +296,77 @@ import UIKit
|
||||
|
||||
var selectNextField = false
|
||||
|
||||
for field in digitFields {
|
||||
if field.digitField == currentTextField {
|
||||
selectNextField = true
|
||||
field.isSelected = false
|
||||
for (index, field) in digitBoxes.enumerated() {
|
||||
|
||||
if field.isSelected {
|
||||
if index == digitBoxes.count - 1 {
|
||||
return
|
||||
} else {
|
||||
selectNextField = true
|
||||
field.isSelected = false
|
||||
}
|
||||
|
||||
} else if selectNextField {
|
||||
if !clear {
|
||||
switchFieldsAutomatically = true
|
||||
}
|
||||
field.isSelected = true
|
||||
field.becomeFirstResponder()
|
||||
field.digitField.becomeFirstResponder()
|
||||
switchFieldsAutomatically = false
|
||||
|
||||
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
|
||||
extension DigitEntryField {
|
||||
|
||||
|
||||
@objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
|
||||
textField.resignFirstResponder()
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldReturn?(textField) ?? true
|
||||
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
|
||||
}
|
||||
|
||||
if !MVMCoreUIUtility.validate(string, withRegularExpression: RegularExpressionDigitOnly) {
|
||||
return false
|
||||
}
|
||||
@ -329,7 +400,7 @@ extension DigitEntryField {
|
||||
return false
|
||||
}
|
||||
|
||||
func textFieldDidDelete(_ textField: UITextField?) {
|
||||
func digitFieldDidDelete(_ textField: UITextField?) {
|
||||
|
||||
// Empty cell, go back to previous cell and clear.
|
||||
selectPreviousDigitField(textField, clear: true)
|
||||
@ -337,34 +408,46 @@ extension DigitEntryField {
|
||||
|
||||
@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 {
|
||||
textField.text = ""
|
||||
valueChanged()
|
||||
}
|
||||
|
||||
uiTextFieldDelegate?.textFieldDidBeginEditing?(textField)
|
||||
proprietorTextDelegate?.textFieldDidBeginEditing?(textField)
|
||||
}
|
||||
|
||||
@objc public func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
|
||||
uiTextFieldDelegate?.textFieldDidEndEditing?(textField)
|
||||
proprietorTextDelegate?.textFieldDidEndEditing?(textField)
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldClear(_ textField: UITextField) -> Bool {
|
||||
|
||||
selectPreviousDigitField(textField, clear: false)
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldClear?(textField) ?? true
|
||||
return proprietorTextDelegate?.textFieldShouldClear?(textField) ?? true
|
||||
}
|
||||
|
||||
@objc public func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
|
||||
|
||||
return uiTextFieldDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
return proprietorTextDelegate?.textFieldShouldBeginEditing?(textField) ?? true
|
||||
}
|
||||
|
||||
@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())
|
||||
|
||||
if !dictionary.isEmpty{
|
||||
for digitBox in digitFields {
|
||||
for digitBox in digitBoxes {
|
||||
MVMCoreUICommonViewsUtility.addDismissToolbar(digitBox.digitField, delegate: delegateObject as? UITextFieldDelegate)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,8 +27,8 @@ import UIKit
|
||||
return label
|
||||
}()
|
||||
|
||||
public private(set) var entryContainer: FormView = {
|
||||
let view = FormView()
|
||||
public private(set) var entryContainer: FormFieldContainer = {
|
||||
let view = FormFieldContainer()
|
||||
view.isAccessibilityElement = false
|
||||
return view
|
||||
}()
|
||||
@ -63,6 +63,11 @@ import UIKit
|
||||
|
||||
/// Toggles error or original UI.
|
||||
public var showError = false {
|
||||
willSet {
|
||||
isLocked = false
|
||||
isSelected = false
|
||||
isEnabled = false
|
||||
}
|
||||
didSet {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@ -75,6 +80,11 @@ import UIKit
|
||||
|
||||
/// Toggles enabled (original) or disabled UI.
|
||||
public var isEnabled = true {
|
||||
willSet {
|
||||
isLocked = false
|
||||
isSelected = false
|
||||
showError = false
|
||||
}
|
||||
didSet {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@ -89,6 +99,11 @@ import UIKit
|
||||
|
||||
/// Toggles original or locked UI.
|
||||
public var isLocked = false {
|
||||
willSet {
|
||||
isEnabled = true
|
||||
isSelected = false
|
||||
showError = false
|
||||
}
|
||||
didSet {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@ -101,6 +116,11 @@ import UIKit
|
||||
|
||||
/// Toggles selected or original (unselected) UI.
|
||||
public var isSelected = false {
|
||||
willSet {
|
||||
isLocked = false
|
||||
isEnabled = true
|
||||
showError = false
|
||||
}
|
||||
didSet {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
@ -61,10 +61,10 @@ import MVMCore
|
||||
}
|
||||
|
||||
/// - parameter bothDelegates: Sets both MF/UI Text Field Delegates.
|
||||
public override init(bothDelegates: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
|
||||
super.init(frame: .zero)
|
||||
setBothTextDelegates(to: bothDelegates)
|
||||
}
|
||||
public override init(bothDelegates: (UITextFieldDelegate & ObservingTextFieldDelegate)?) {
|
||||
super.init(frame: .zero)
|
||||
setBothTextDelegates(to: bothDelegates)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
@ -238,7 +238,7 @@ import UIKit
|
||||
|
||||
if isValid {
|
||||
clearErrorState()
|
||||
entryContainer.bottomBar.backgroundColor = UIColor.black.cgColor
|
||||
entryContainer.bottomBar?.backgroundColor = UIColor.black.cgColor
|
||||
|
||||
} else if let errMessage = errorMessage {
|
||||
feedback = errMessage
|
||||
|
||||
@ -8,6 +8,10 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
protocol TextFieldDelegate {
|
||||
func textFieldDidDelete()
|
||||
}
|
||||
|
||||
|
||||
open class TextField: UITextField {
|
||||
//--------------------------------------------------
|
||||
@ -21,6 +25,14 @@ open class TextField: UITextField {
|
||||
/// Set to true to hide the blinking textField cursor.
|
||||
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
|
||||
//--------------------------------------------------
|
||||
@ -55,6 +67,11 @@ open class TextField: UITextField {
|
||||
|
||||
return super.caretRect(for: position)
|
||||
}
|
||||
|
||||
open override func deleteBackward() {
|
||||
super.deleteBackward()
|
||||
// proprietorTextDelegate?.textFieldDidDelete()
|
||||
}
|
||||
}
|
||||
|
||||
/// MARK:- MVMCoreViewProtocol
|
||||
|
||||
@ -9,13 +9,13 @@
|
||||
import UIKit
|
||||
|
||||
|
||||
@objcMembers open class FormView: View {
|
||||
@objcMembers open class FormFieldContainer: View {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Drawing Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
/// The bottom border line.
|
||||
public var bottomBar: CAShapeLayer = {
|
||||
/// The bottom border line. Height is dynamic based on scenario.
|
||||
public var bottomBar: CAShapeLayer? = {
|
||||
let layer = CAShapeLayer()
|
||||
layer.backgroundColor = UIColor.black.cgColor
|
||||
layer.drawsAsynchronously = true
|
||||
@ -23,8 +23,15 @@ import UIKit
|
||||
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.
|
||||
public var hideBorders = false
|
||||
private var hideBorders = false
|
||||
|
||||
public var borderStrokeColor: UIColor = .mfSilver()
|
||||
private var borderPath: UIBezierPath = UIBezierPath()
|
||||
@ -35,17 +42,13 @@ import UIKit
|
||||
|
||||
public var showError = false {
|
||||
didSet {
|
||||
if !hideBorders {
|
||||
showError ? errorUI() : originalUI()
|
||||
}
|
||||
showError ? errorUI() : originalUI()
|
||||
}
|
||||
}
|
||||
|
||||
public var isEnabled = true {
|
||||
didSet {
|
||||
if !hideBorders {
|
||||
isEnabled ? originalUI() : disabledUI()
|
||||
}
|
||||
isEnabled ? originalUI() : disabledUI()
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,9 +60,7 @@ import UIKit
|
||||
|
||||
public var isSelected = false {
|
||||
didSet {
|
||||
if !hideBorders {
|
||||
isSelected ? selectedUI() : originalUI()
|
||||
}
|
||||
isSelected ? selectedUI() : originalUI()
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +93,7 @@ import UIKit
|
||||
|
||||
borderPath.removeAllPoints()
|
||||
|
||||
if !hideBorders {
|
||||
if !disableBorders && !hideBorders {
|
||||
// Brings the other half of the line inside the view to prevent cropping.
|
||||
let origin = bounds.origin
|
||||
let size = frame.size
|
||||
@ -113,46 +114,75 @@ import UIKit
|
||||
super.setupView()
|
||||
|
||||
isOpaque = false
|
||||
layer.addSublayer(bottomBar)
|
||||
if let bottomBar = bottomBar {
|
||||
layer.addSublayer(bottomBar)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// 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() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .mfSilver()
|
||||
bottomBar.backgroundColor = UIColor.black.cgColor
|
||||
bottomBar?.backgroundColor = UIColor.black.cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func errorUI() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .mfPumpkin()
|
||||
bottomBar.backgroundColor = UIColor.mfPumpkin().cgColor
|
||||
hideBorders = false
|
||||
bottomBar?.backgroundColor = UIColor.mfPumpkin().cgColor
|
||||
refreshUI(bottomBarSize: 4)
|
||||
}
|
||||
|
||||
open func selectedUI() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
hideBorders = false
|
||||
borderStrokeColor = .black
|
||||
bottomBar.backgroundColor = UIColor.black.cgColor
|
||||
hideBorders = false
|
||||
bottomBar?.backgroundColor = UIColor.black.cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func lockedUI() {
|
||||
|
||||
isUserInteractionEnabled = false
|
||||
hideBorders = true
|
||||
borderStrokeColor = .clear
|
||||
bottomBar.backgroundColor = UIColor.clear.cgColor
|
||||
hideBorders = true
|
||||
bottomBar?.backgroundColor = UIColor.clear.cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
@ -160,15 +190,16 @@ import UIKit
|
||||
|
||||
isUserInteractionEnabled = false
|
||||
borderStrokeColor = .mfSilver()
|
||||
bottomBar.backgroundColor = UIColor.mfSilver().cgColor
|
||||
hideBorders = false
|
||||
bottomBar?.backgroundColor = UIColor.mfSilver().cgColor
|
||||
refreshUI(bottomBarSize: 1)
|
||||
}
|
||||
|
||||
open func refreshUI(bottomBarSize: CGFloat? = nil) {
|
||||
|
||||
if !hideBorders {
|
||||
if !disableBorders {
|
||||
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)
|
||||
setNeedsDisplay()
|
||||
@ -186,8 +217,8 @@ import UIKit
|
||||
|
||||
guard let dictionary = json, !dictionary.isEmpty else { return }
|
||||
|
||||
if let hideBorders = dictionary["hideBorders"] as? Bool {
|
||||
self.hideBorders = hideBorders
|
||||
if let disableBorders = dictionary["disableBorders"] as? Bool {
|
||||
self.disableBorders = disableBorders
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user