latestest and greatest.
This commit is contained in:
parent
702dc10db2
commit
26c73070d9
@ -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
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 }
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user