refactored the rest of the inputfield code into the fieldtype handlers
Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
parent
a656561073
commit
4ed2b3c894
@ -75,51 +75,121 @@ extension InputField {
|
||||
}
|
||||
}
|
||||
|
||||
class CreditCardHandler: BaseFieldType {
|
||||
class CreditCardHandler: FieldTypeHandler {
|
||||
static let shared = CreditCardHandler()
|
||||
|
||||
|
||||
var creditCardType: CreditCardType = .generic
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
self.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
override func updateView(_ inputField: InputField) {
|
||||
minWidth = 288.0
|
||||
leftImageName = creditCardType.imageName
|
||||
|
||||
super.updateView(inputField)
|
||||
}
|
||||
|
||||
override func appendRules(for inputField: InputField) {
|
||||
override func appendRules(_ inputField: InputField) {
|
||||
if let text = inputField.textField.text, text.count > 0 {
|
||||
let rule = CharacterCountRule().copyWith {
|
||||
$0.maxLength = inputField.creditCardType.maxLength
|
||||
$0.maxLength = creditCardType.maxLength
|
||||
$0.compareType = .equals
|
||||
$0.errorMessage = "Enter a valid credit card."
|
||||
}
|
||||
inputField.rules.append(.init(rule))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal func formatCreditCardNumber(_ number: String) -> String {
|
||||
let formattedInput = number.filter { $0.isNumber } // Remove any existing slashes
|
||||
return String.format(formattedInput, indices: creditCardType.separatorIndices, with: " ")
|
||||
}
|
||||
|
||||
internal func updateCardTypeIcon(rawNumber: String) {
|
||||
guard rawNumber.count >= 4,
|
||||
let firstFourDigits = Int(String(rawNumber.prefix(4))),
|
||||
let creditCardType = CreditCardType.from(iin: firstFourDigits) else {
|
||||
leftImageView.image = BundleManager.shared.image(for: CreditCardType.generic.imageName)
|
||||
creditCardType = .generic
|
||||
return
|
||||
override func textFieldDidBeginEditing(_ inputField: InputField, textField: UITextField) {
|
||||
if let value {
|
||||
textField.text = formatCreditCardNumber(value)
|
||||
}
|
||||
}
|
||||
self.creditCardType = creditCardType
|
||||
}
|
||||
|
||||
override func textFieldDidEndEditing(_ inputField: InputField, textField: UITextField) {
|
||||
if let value {
|
||||
textField.text = maskCreditCardNumber(value)
|
||||
}
|
||||
}
|
||||
|
||||
override func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
let allowedCharacters = CharacterSet.decimalDigits
|
||||
if string.rangeOfCharacter(from: allowedCharacters.inverted) != nil && !string.isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
internal func maskCreditCardNumber(_ number: String) -> String {
|
||||
// Mask the first 12 characters if the length is 16
|
||||
let rawNumber = number.filter { $0.isNumber }
|
||||
guard rawNumber.count == creditCardType.maxLength else { return formatCreditCardNumber(number) }
|
||||
let lastFourDigits = rawNumber.suffix(4)
|
||||
let maskedSection = String(repeating: "•", count: 12)
|
||||
let formattedMaskSection = String.format(maskedSection, indices: creditCardType.separatorIndices, with: " ")
|
||||
return formattedMaskSection + " " + lastFourDigits
|
||||
// Get the current text
|
||||
let currentText = textField.text ?? ""
|
||||
|
||||
// Calculate the new text
|
||||
let newText = (currentText as NSString).replacingCharacters(in: range, with: string)
|
||||
|
||||
// Remove any existing formatting
|
||||
let rawNumber = newText.filter { $0.isNumber }
|
||||
|
||||
if rawNumber.count > creditCardType.maxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
// Format the number with spaces
|
||||
let formattedNumber = formatCreditCardNumber(rawNumber)
|
||||
|
||||
// Update the icon based on the first four digits
|
||||
updateCardTypeIcon(inputField, rawNumber: rawNumber)
|
||||
|
||||
// Check again
|
||||
if rawNumber.count > creditCardType.maxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
// Set the formatted text
|
||||
textField.text = formattedNumber
|
||||
|
||||
// Calculate the new cursor position
|
||||
if let newPosition = textField.cursorPosition(range: range,
|
||||
replacementString: string,
|
||||
rawNumber: rawNumber,
|
||||
formattedNumber: formattedNumber) {
|
||||
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
|
||||
}
|
||||
|
||||
// if all passes, then set the number1
|
||||
value = rawNumber
|
||||
|
||||
// Prevent the default behavior
|
||||
return false
|
||||
}
|
||||
|
||||
/// Private
|
||||
internal func formatCreditCardNumber(_ number: String) -> String {
|
||||
let formattedInput = number.filter { $0.isNumber } // Remove any existing slashes
|
||||
return String.format(formattedInput, indices: creditCardType.separatorIndices, with: " ")
|
||||
}
|
||||
|
||||
internal func updateCardTypeIcon(_ inputField: InputField, rawNumber: String) {
|
||||
defer { inputField.setNeedsUpdate() }
|
||||
|
||||
guard rawNumber.count >= 4,
|
||||
let firstFourDigits = Int(String(rawNumber.prefix(4))),
|
||||
let creditCardType = CreditCardType.from(iin: firstFourDigits) else {
|
||||
creditCardType = .generic
|
||||
return
|
||||
}
|
||||
|
||||
self.creditCardType = creditCardType
|
||||
}
|
||||
|
||||
internal func maskCreditCardNumber(_ number: String) -> String {
|
||||
// Mask the first 12 characters if the length is 16
|
||||
let rawNumber = number.filter { $0.isNumber }
|
||||
guard rawNumber.count == creditCardType.maxLength else { return formatCreditCardNumber(number) }
|
||||
let lastFourDigits = rawNumber.suffix(4)
|
||||
let maskedSection = String(repeating: "•", count: 12)
|
||||
let formattedMaskSection = String.format(maskedSection, indices: creditCardType.separatorIndices, with: " ")
|
||||
return formattedMaskSection + " " + lastFourDigits
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import UIKit
|
||||
|
||||
extension InputField {
|
||||
|
||||
class DateHandler: BaseFieldType {
|
||||
class DateHandler: FieldTypeHandler {
|
||||
static let shared = DateHandler()
|
||||
|
||||
private override init() {
|
||||
@ -18,9 +18,14 @@ extension InputField {
|
||||
self.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
override func updateView(_ inputField: InputField) {
|
||||
minWidth = 114.0
|
||||
placeholderText = inputField.dateFormat.placeholderText
|
||||
|
||||
super.updateView(inputField)
|
||||
}
|
||||
|
||||
override func appendRules(for inputField: InputField) {
|
||||
override func appendRules(_ inputField: InputField) {
|
||||
if let text = inputField.textField.text, text.count > 0 {
|
||||
let rule = CharacterCountRule().copyWith {
|
||||
$0.maxLength = inputField.dateFormat.maxLength
|
||||
@ -30,6 +35,27 @@ extension InputField {
|
||||
inputField.rules.append(.init(rule))
|
||||
}
|
||||
}
|
||||
|
||||
override func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
// Allow only numbers and limit the length of text.
|
||||
guard let oldText = textField.text,
|
||||
let textRange = Range(range, in: oldText),
|
||||
string.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil else {
|
||||
return false
|
||||
}
|
||||
|
||||
let newText = oldText.replacingCharacters(in: textRange, with: string)
|
||||
if newText.count > inputField.dateFormat.maxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
if newText.count <= inputField.dateFormat.maxLength {
|
||||
textField.text = String.format(newText, indices: inputField.dateFormat.separatorIndices, with: "/")
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum DateFormat: String, CaseIterable {
|
||||
@ -69,21 +95,5 @@ extension InputField {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal func formatDate(_ input: String) -> String {
|
||||
let formattedInput = input.filter { $0.isNumber } // Remove any existing slashes
|
||||
var formattedString = ""
|
||||
var currentIndex = formattedInput.startIndex
|
||||
|
||||
for index in 0..<formattedInput.count {
|
||||
if dateFormat.separatorIndices.contains(index) {
|
||||
formattedString.append("/")
|
||||
}
|
||||
formattedString.append(formattedInput[currentIndex])
|
||||
currentIndex = formattedInput.index(after: currentIndex)
|
||||
}
|
||||
|
||||
return formattedString
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,20 +10,7 @@ import UIKit
|
||||
import VDSTokens
|
||||
|
||||
extension InputField {
|
||||
protocol FieldTypeHandler: UITextFieldDelegate {
|
||||
var keyboardType: UIKeyboardType { get }
|
||||
var minWidth: CGFloat { get set }
|
||||
var leftImageName: String? { get set }
|
||||
var actionModel: InputField.TextLinkModel? { get set }
|
||||
var toolTipModel: Tooltip.TooltipModel? { get set }
|
||||
var isSecureTextEntry: Bool { get set }
|
||||
var placeholderText: String? { get set }
|
||||
|
||||
func configure(for inputField: InputField)
|
||||
func appendRules(for inputField: InputField)
|
||||
}
|
||||
|
||||
class BaseFieldType: NSObject, FieldTypeHandler {
|
||||
class FieldTypeHandler: NSObject {
|
||||
var keyboardType: UIKeyboardType
|
||||
var minWidth: CGFloat = 40.0
|
||||
var leftImageName: String?
|
||||
@ -31,13 +18,14 @@ extension InputField {
|
||||
var toolTipModel: Tooltip.TooltipModel?
|
||||
var isSecureTextEntry = false
|
||||
var placeholderText: String?
|
||||
|
||||
var value: String?
|
||||
|
||||
internal override init() {
|
||||
keyboardType = .default
|
||||
super.init()
|
||||
}
|
||||
|
||||
func configure(for inputField: InputField) {
|
||||
func updateView(_ inputField: InputField) {
|
||||
|
||||
//textField
|
||||
inputField.textField.isSecureTextEntry = isSecureTextEntry
|
||||
@ -78,8 +66,18 @@ extension InputField {
|
||||
inputField.tooltipModel = toolTipModel
|
||||
}
|
||||
|
||||
func appendRules(for inputField: InputField) {}
|
||||
func appendRules(_ inputField: InputField) {}
|
||||
|
||||
func textFieldDidBeginEditing(_ inputField: InputField, textField: UITextField) {
|
||||
}
|
||||
|
||||
func textFieldDidEndEditing(_ inputField: InputField, textField: UITextField) {
|
||||
}
|
||||
|
||||
func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum FieldType: String, CaseIterable {
|
||||
|
||||
@ -10,16 +10,18 @@ import UIKit
|
||||
|
||||
extension InputField {
|
||||
|
||||
class InlineActionHandler: BaseFieldType {
|
||||
class InlineActionHandler: FieldTypeHandler {
|
||||
static let shared = InlineActionHandler()
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
|
||||
override func appendRules(for inputField: InputField) {}
|
||||
override func updateView(_ inputField: InputField) {
|
||||
minWidth = 102.0
|
||||
|
||||
super.updateView(inputField)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import UIKit
|
||||
|
||||
extension InputField {
|
||||
|
||||
class NumberHandler: BaseFieldType {
|
||||
class NumberHandler: FieldTypeHandler {
|
||||
static let shared = NumberHandler()
|
||||
|
||||
private override init() {
|
||||
@ -18,10 +18,12 @@ extension InputField {
|
||||
self.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
|
||||
override func appendRules(for inputField: InputField) {}
|
||||
|
||||
override func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
// Allow only numbers
|
||||
let allowedCharacters = CharacterSet.decimalDigits
|
||||
let characterSet = CharacterSet(charactersIn: string)
|
||||
return allowedCharacters.isSuperset(of: characterSet)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -18,16 +18,37 @@ extension InputField {
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordHandler: BaseFieldType {
|
||||
class PasswordHandler: FieldTypeHandler {
|
||||
static let shared = PasswordHandler()
|
||||
|
||||
internal var passwordActionType: PasswordAction = .hide
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
|
||||
override func appendRules(for inputField: InputField) {}
|
||||
override func updateView(_ inputField: InputField) {
|
||||
let isHide = passwordActionType == .hide
|
||||
let buttonText = isHide ?
|
||||
inputField.hidePasswordButtonText.isEmpty ? "Hide" : inputField.hidePasswordButtonText :
|
||||
inputField.showPasswordButtonText.isEmpty ? "Show" : inputField.showPasswordButtonText
|
||||
|
||||
isSecureTextEntry = !isHide
|
||||
let nextPasswordActionType = passwordActionType.toggle()
|
||||
if let text = inputField.text, !text.isEmpty {
|
||||
actionModel = .init(text: buttonText,
|
||||
onClick: { [weak self] _ in
|
||||
guard let self else { return }
|
||||
self.passwordActionType = nextPasswordActionType
|
||||
inputField.setNeedsUpdate()
|
||||
})
|
||||
} else {
|
||||
passwordActionType = .show
|
||||
}
|
||||
minWidth = 62.0
|
||||
|
||||
super.updateView(inputField)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import UIKit
|
||||
|
||||
extension InputField {
|
||||
|
||||
class SecurityCodeHandler: BaseFieldType {
|
||||
class SecurityCodeHandler: FieldTypeHandler {
|
||||
static let shared = SecurityCodeHandler()
|
||||
|
||||
private override init() {
|
||||
@ -18,9 +18,19 @@ extension InputField {
|
||||
self.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
override func updateView(_ inputField: InputField) {
|
||||
minWidth = 88.0
|
||||
isSecureTextEntry = true
|
||||
|
||||
super.updateView(inputField)
|
||||
}
|
||||
|
||||
override func appendRules(for inputField: InputField) {}
|
||||
override func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
// Allow only numbers and limit the length of text.
|
||||
let allowedCharacters = CharacterSet.decimalDigits
|
||||
let characterSet = CharacterSet(charactersIn: string)
|
||||
return allowedCharacters.isSuperset(of: characterSet) && ((textField.text?.count ?? 0) + string.count - range.length) <= 4
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,38 +10,7 @@ import UIKit
|
||||
|
||||
extension InputField {
|
||||
|
||||
internal func formatUSNumber(_ number: String) -> String {
|
||||
// Format the number in the style XXX-XXX-XXXX
|
||||
let areaCodeLength = 3
|
||||
let centralOfficeCodeLength = 3
|
||||
let lineNumberLength = 4
|
||||
|
||||
var formattedNumber = ""
|
||||
|
||||
if number.count > 0 {
|
||||
formattedNumber.append(contentsOf: number.prefix(areaCodeLength))
|
||||
}
|
||||
|
||||
if number.count > areaCodeLength {
|
||||
let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength)
|
||||
let endIndex = number.index(startIndex, offsetBy: min(centralOfficeCodeLength, number.count - areaCodeLength))
|
||||
let centralOfficeCode = number[startIndex..<endIndex]
|
||||
formattedNumber.append("-")
|
||||
formattedNumber.append(contentsOf: centralOfficeCode)
|
||||
}
|
||||
|
||||
if number.count > areaCodeLength + centralOfficeCodeLength {
|
||||
let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength + centralOfficeCodeLength)
|
||||
let endIndex = number.index(startIndex, offsetBy: min(lineNumberLength, number.count - areaCodeLength - centralOfficeCodeLength))
|
||||
let lineNumber = number[startIndex..<endIndex]
|
||||
formattedNumber.append("-")
|
||||
formattedNumber.append(contentsOf: lineNumber)
|
||||
}
|
||||
|
||||
return formattedNumber
|
||||
}
|
||||
|
||||
class TelephoneHandler: BaseFieldType {
|
||||
class TelephoneHandler: FieldTypeHandler {
|
||||
static let shared = TelephoneHandler()
|
||||
|
||||
private override init() {
|
||||
@ -49,9 +18,13 @@ extension InputField {
|
||||
self.keyboardType = .phonePad
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
override func updateView(_ inputField: InputField) {
|
||||
minWidth = 176.0
|
||||
|
||||
super.updateView(inputField)
|
||||
}
|
||||
|
||||
override func appendRules(for inputField: InputField) {
|
||||
override func appendRules(_ inputField: InputField) {
|
||||
if let text = inputField.textField.text, text.count > 0 {
|
||||
let rule = CharacterCountRule().copyWith {
|
||||
$0.maxLength = "XXX-XXX-XXXX".count
|
||||
@ -61,6 +34,70 @@ extension InputField {
|
||||
inputField.rules.append(.init(rule))
|
||||
}
|
||||
}
|
||||
|
||||
override func textField(_ inputField: InputField, textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
// Allow only numbers and limit the length of text.
|
||||
let allowedCharacters = CharacterSet(charactersIn: "01233456789")
|
||||
let characterSet = CharacterSet(charactersIn: string)
|
||||
let currentText = textField.text ?? ""
|
||||
if !allowedCharacters.isSuperset(of: characterSet) { return false }
|
||||
|
||||
// Calculate the new text
|
||||
let newText = (currentText as NSString).replacingCharacters(in: range, with: string)
|
||||
|
||||
// Remove any existing formatting
|
||||
let rawNumber = newText.filter { $0.isNumber }
|
||||
|
||||
// Format the number with dashes
|
||||
let formattedNumber = formatUSNumber(rawNumber)
|
||||
|
||||
// Set the formatted text
|
||||
textField.text = formattedNumber
|
||||
|
||||
// Calculate the new cursor position
|
||||
if let newPosition = textField.cursorPosition(range: range,
|
||||
replacementString: string,
|
||||
rawNumber: rawNumber,
|
||||
formattedNumber: formattedNumber) {
|
||||
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
|
||||
}
|
||||
|
||||
// Prevent the default behavior
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
internal func formatUSNumber(_ number: String) -> String {
|
||||
// Format the number in the style XXX-XXX-XXXX
|
||||
let areaCodeLength = 3
|
||||
let centralOfficeCodeLength = 3
|
||||
let lineNumberLength = 4
|
||||
|
||||
var formattedNumber = ""
|
||||
|
||||
if number.count > 0 {
|
||||
formattedNumber.append(contentsOf: number.prefix(areaCodeLength))
|
||||
}
|
||||
|
||||
if number.count > areaCodeLength {
|
||||
let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength)
|
||||
let endIndex = number.index(startIndex, offsetBy: min(centralOfficeCodeLength, number.count - areaCodeLength))
|
||||
let centralOfficeCode = number[startIndex..<endIndex]
|
||||
formattedNumber.append("-")
|
||||
formattedNumber.append(contentsOf: centralOfficeCode)
|
||||
}
|
||||
|
||||
if number.count > areaCodeLength + centralOfficeCodeLength {
|
||||
let startIndex = number.index(number.startIndex, offsetBy: areaCodeLength + centralOfficeCodeLength)
|
||||
let endIndex = number.index(startIndex, offsetBy: min(lineNumberLength, number.count - areaCodeLength - centralOfficeCodeLength))
|
||||
let lineNumber = number[startIndex..<endIndex]
|
||||
formattedNumber.append("-")
|
||||
formattedNumber.append(contentsOf: lineNumber)
|
||||
}
|
||||
|
||||
return formattedNumber
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,15 +9,11 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
extension InputField {
|
||||
class TextHandler: BaseFieldType {
|
||||
class TextHandler: FieldTypeHandler {
|
||||
static let shared = TextHandler()
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func configure(for inputField: InputField) {}
|
||||
|
||||
override func appendRules(for inputField: InputField) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,8 +86,8 @@ open class InputField: EntryFieldBase {
|
||||
|
||||
/// Value for the textField
|
||||
open override var value: String? {
|
||||
if fieldType == .creditCard {
|
||||
return creditCardRawNumber
|
||||
if let value = fieldType.handler().value {
|
||||
return value
|
||||
} else {
|
||||
return textField.text
|
||||
}
|
||||
@ -205,7 +205,7 @@ open class InputField: EntryFieldBase {
|
||||
open override func updateView() {
|
||||
|
||||
//update fieldType first
|
||||
updateFieldType()
|
||||
fieldType.handler().updateView(self)
|
||||
|
||||
super.updateView()
|
||||
|
||||
@ -254,103 +254,10 @@ open class InputField: EntryFieldBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open func updateFieldType() {
|
||||
fieldType.handler().configure(for: self)
|
||||
|
||||
var minWidth: CGFloat = 40.0
|
||||
var leftImageName: String?
|
||||
var actionModel: InputField.TextLinkModel?
|
||||
var toolTipModel: Tooltip.TooltipModel? = tooltipModel
|
||||
var isSecureTextEntry = false
|
||||
var placeholderText: String?
|
||||
|
||||
switch fieldType {
|
||||
case .text:
|
||||
break
|
||||
|
||||
case .number:
|
||||
break
|
||||
|
||||
case .inlineAction:
|
||||
minWidth = 102.0
|
||||
|
||||
case .password:
|
||||
let isHide = passwordActionType == .hide
|
||||
let buttonText = isHide ?
|
||||
hidePasswordButtonText.isEmpty ? "Hide" : hidePasswordButtonText :
|
||||
showPasswordButtonText.isEmpty ? "Show" : showPasswordButtonText
|
||||
|
||||
isSecureTextEntry = !isHide
|
||||
let nextPasswordActionType = passwordActionType.toggle()
|
||||
if let text, !text.isEmpty {
|
||||
actionModel = .init(text: buttonText,
|
||||
onClick: { [weak self] _ in
|
||||
guard let self else { return }
|
||||
self.passwordActionType = nextPasswordActionType
|
||||
})
|
||||
} else {
|
||||
passwordActionType = .show
|
||||
}
|
||||
minWidth = 62.0
|
||||
|
||||
case .creditCard:
|
||||
minWidth = 288.0
|
||||
leftImageName = creditCardType.imageName
|
||||
case .telephone:
|
||||
minWidth = 176.0
|
||||
|
||||
case .date:
|
||||
minWidth = 114.0
|
||||
placeholderText = dateFormat.placeholderText
|
||||
case .securityCode:
|
||||
minWidth = 88.0
|
||||
isSecureTextEntry = true
|
||||
}
|
||||
|
||||
//textField
|
||||
textField.isSecureTextEntry = isSecureTextEntry
|
||||
|
||||
//leftIcon
|
||||
if let leftImageName {
|
||||
leftImageView.image = BundleManager.shared.image(for: leftImageName)?.withTintColor(iconColorConfiguration.getColor(self))
|
||||
}
|
||||
leftImageView.isHidden = leftImageName == nil
|
||||
|
||||
//actionLink
|
||||
actionTextLink.surface = surface
|
||||
if let actionModel {
|
||||
actionTextLink.text = actionModel.text
|
||||
actionTextLink.onClick = actionModel.onClick
|
||||
actionTextLink.isHidden = false
|
||||
containerStackView.setCustomSpacing(VDSLayout.space2X, after: statusIcon)
|
||||
} else {
|
||||
actionTextLink.isHidden = true
|
||||
containerStackView.setCustomSpacing(0, after: statusIcon)
|
||||
}
|
||||
|
||||
//set the width constraints
|
||||
if let width, width > minWidth {
|
||||
widthConstraint?.constant = width
|
||||
widthConstraint?.isActive = true
|
||||
minWidthConstraint?.isActive = false
|
||||
} else {
|
||||
minWidthConstraint?.constant = minWidth
|
||||
widthConstraint?.isActive = false
|
||||
minWidthConstraint?.isActive = true
|
||||
}
|
||||
|
||||
//placeholder
|
||||
textField.placeholder = placeholderText
|
||||
|
||||
//tooltip
|
||||
tooltipModel = toolTipModel
|
||||
|
||||
}
|
||||
|
||||
override func updateRules() {
|
||||
super.updateRules()
|
||||
fieldType.handler().appendRules(for: self)
|
||||
fieldType.handler().appendRules(self)
|
||||
}
|
||||
|
||||
/// Used to update any Accessibility properties.
|
||||
@ -390,10 +297,14 @@ open class InputField: EntryFieldBase {
|
||||
}
|
||||
return super.resignFirstResponder()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Public FieldType Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Password
|
||||
//--------------------------------------------------
|
||||
internal var passwordActionType: PasswordAction = .show { didSet { setNeedsUpdate() } }
|
||||
open var hidePasswordButtonText: String = "Hide" { didSet { setNeedsUpdate() } }
|
||||
open var showPasswordButtonText: String = "Show" { didSet { setNeedsUpdate() } }
|
||||
|
||||
@ -402,149 +313,20 @@ open class InputField: EntryFieldBase {
|
||||
//--------------------------------------------------
|
||||
open var dateFormat: DateFormat = .mmddyy { didSet { setNeedsUpdate() } }
|
||||
|
||||
//---------------------------------------------------
|
||||
// MARK: - Credit Card
|
||||
//---------------------------------------------------
|
||||
internal var creditCardRawNumber: String = ""
|
||||
internal var creditCardType: CreditCardType = .generic { didSet { setNeedsUpdate() } }
|
||||
|
||||
//---------------------------------------------------
|
||||
// MARK: - Telephone
|
||||
//---------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
extension InputField: UITextFieldDelegate {
|
||||
public func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
if fieldType == .creditCard {
|
||||
textField.text = formatCreditCardNumber(creditCardRawNumber)
|
||||
}
|
||||
fieldType.handler().textFieldDidBeginEditing(self, textField: textField)
|
||||
}
|
||||
|
||||
public func textFieldDidEndEditing(_ textField: UITextField) {
|
||||
if self.fieldType == .creditCard {
|
||||
textField.text = maskCreditCardNumber(creditCardRawNumber)
|
||||
}
|
||||
fieldType.handler().textFieldDidEndEditing(self, textField: textField)
|
||||
validate()
|
||||
}
|
||||
|
||||
public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
|
||||
// case text, number, inlineAction, password, creditCard, tel, date, securityCode
|
||||
|
||||
switch fieldType {
|
||||
case .creditCard:
|
||||
let allowedCharacters = CharacterSet.decimalDigits
|
||||
if string.rangeOfCharacter(from: allowedCharacters.inverted) != nil && !string.isEmpty {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the current text
|
||||
let currentText = textField.text ?? ""
|
||||
|
||||
// Calculate the new text
|
||||
let newText = (currentText as NSString).replacingCharacters(in: range, with: string)
|
||||
|
||||
// Remove any existing formatting
|
||||
let rawNumber = newText.filter { $0.isNumber }
|
||||
|
||||
if rawNumber.count > creditCardType.maxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
// Format the number with spaces
|
||||
let formattedNumber = formatCreditCardNumber(rawNumber)
|
||||
|
||||
// Update the icon based on the first four digits
|
||||
updateCardTypeIcon(rawNumber: rawNumber)
|
||||
|
||||
// Check again
|
||||
if rawNumber.count > creditCardType.maxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
// Set the formatted text
|
||||
textField.text = formattedNumber
|
||||
|
||||
// Calculate the new cursor position
|
||||
if let newPosition = textField.cursorPosition(range: range,
|
||||
replacementString: string,
|
||||
rawNumber: rawNumber,
|
||||
formattedNumber: formattedNumber) {
|
||||
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
|
||||
}
|
||||
|
||||
// if all passes, then set the number1
|
||||
creditCardRawNumber = rawNumber
|
||||
|
||||
// Prevent the default behavior
|
||||
return false
|
||||
|
||||
case .date:
|
||||
// Allow only numbers and limit the length of text.
|
||||
guard let oldText = textField.text,
|
||||
let textRange = Range(range, in: oldText),
|
||||
string.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil else {
|
||||
return false
|
||||
}
|
||||
|
||||
let newText = oldText.replacingCharacters(in: textRange, with: string)
|
||||
if newText.count > dateFormat.maxLength {
|
||||
return false
|
||||
}
|
||||
|
||||
if newText.count <= dateFormat.maxLength {
|
||||
textField.text = formatDate(newText)
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
|
||||
case .number:
|
||||
// Allow only numbers
|
||||
let allowedCharacters = CharacterSet.decimalDigits
|
||||
let characterSet = CharacterSet(charactersIn: string)
|
||||
return allowedCharacters.isSuperset(of: characterSet)
|
||||
|
||||
case .telephone:
|
||||
// Allow only numbers and limit the length of text.
|
||||
let allowedCharacters = CharacterSet(charactersIn: "01233456789")
|
||||
let characterSet = CharacterSet(charactersIn: string)
|
||||
let currentText = textField.text ?? ""
|
||||
if !allowedCharacters.isSuperset(of: characterSet) { return false }
|
||||
|
||||
// Calculate the new text
|
||||
let newText = (currentText as NSString).replacingCharacters(in: range, with: string)
|
||||
|
||||
// Remove any existing formatting
|
||||
let rawNumber = newText.filter { $0.isNumber }
|
||||
|
||||
// Format the number with dashes
|
||||
let formattedNumber = formatUSNumber(rawNumber)
|
||||
|
||||
// Set the formatted text
|
||||
textField.text = formattedNumber
|
||||
|
||||
// Calculate the new cursor position
|
||||
if let newPosition = textField.cursorPosition(range: range,
|
||||
replacementString: string,
|
||||
rawNumber: rawNumber,
|
||||
formattedNumber: formattedNumber) {
|
||||
textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
|
||||
}
|
||||
|
||||
// Prevent the default behavior
|
||||
return false
|
||||
|
||||
case .securityCode:
|
||||
// Allow only numbers and limit the length of text.
|
||||
let allowedCharacters = CharacterSet.decimalDigits
|
||||
let characterSet = CharacterSet(charactersIn: string)
|
||||
return allowedCharacters.isSuperset(of: characterSet) && ((textField.text?.count ?? 0) + string.count - range.length) <= 4
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
return fieldType.handler().textField(self, textField: textField, shouldChangeCharactersIn: range, replacementString: string)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user