I wish to write a customized TextField on SwiftUI to have an enter with bank card format like this “xxxx-xxxx-xxxx-xxxx”, I used to be this solutions https://stackoverflow.com/a/48252437, however I am fighting UIViewRepresentable, once I wish to previous the numbers into the unique textual content, it would not appears to replace it with the format.
The textField will present you 12345, however I would like 1234 5
struct ContentView: View {
@State var cardNumber: String = "12345" // <-- It will not be formatted till I sort one other one quantity
var physique: some View {
VStack {
CreditCardTextField(quantity: $cardNumber)
.body(top: 50)
.border(.black)
}
.padding()
}
}
And UIViewRepresentable the place I transported the code from the hyperlink reply
struct CreditCardTextField: UIViewRepresentable {
@Binding public var quantity: String
public init(quantity: Binding<String>) {
self._number = quantity
}
public func makeUIView(context: Context) -> UITextField {
let view = UITextField()
view.addTarget(context.coordinator, motion: #selector(Coordinator.reformatAsCardNumber), for: .editingChanged)
view.delegate = context.coordinator
return view
}
public func updateUIView(_ uiView: UITextField, context: Context) {
uiView.textual content = quantity // <-- I consider in right here I ought to replace the code in order that it is going to be formatted, however I can not get tips on how to refactore the code
}
public func makeCoordinator() -> Coordinator {
Coordinator($quantity)
}
// MARK: Coordinator
public class Coordinator: NSObject, UITextFieldDelegate {
@Binding var quantity: String
non-public var previousTextFieldContent: String?
non-public var previousSelection: UITextRange?
init(_ quantity: Binding<String>) {
self._number = quantity
}
func textField(_ textField: UITextField, shouldChangeCharactersIn vary: NSRange, replacementString string: String) -> Bool {
previousTextFieldContent = textField.textual content
previousSelection = textField.selectedTextRange
return true
}
@objc func reformatAsCardNumber(textField: UITextField, for occasion: UIControl.Occasion) {
var targetCursorPosition = 0
if let startPosition = textField.selectedTextRange?.begin {
targetCursorPosition = textField.offset(from: textField.beginningOfDocument, to: startPosition)
}
var cardNumberWithoutSpaces = ""
if let textual content = textField.textual content {
cardNumberWithoutSpaces = self.removeNonDigits(string: textual content, andPreserveCursorPosition: &targetCursorPosition)
}
if cardNumberWithoutSpaces.rely > 16 {
textField.textual content = previousTextFieldContent
textField.selectedTextRange = previousSelection
return
}
let cardNumberWithSpaces = self.insertCreditCardSpaces(cardNumberWithoutSpaces, preserveCursorPosition: &targetCursorPosition)
textField.textual content = cardNumberWithSpaces
quantity = cardNumberWithSpaces
if let targetPosition = textField.place(from: textField.beginningOfDocument, offset: targetCursorPosition) {
textField.selectedTextRange = textField.textRange(from: targetPosition, to: targetPosition)
}
}
func removeNonDigits(string: String, andPreserveCursorPosition cursorPosition: inout Int) -> String {
var digitsOnlyString = ""
let originalCursorPosition = cursorPosition
for i in Swift.stride(from: 0, to: string.rely, by: 1) {
let characterToAdd = string[string.index(string.startIndex, offsetBy: i)]
if characterToAdd >= "0" && characterToAdd <= "9" {
digitsOnlyString.append(characterToAdd)
} else if i < originalCursorPosition {
cursorPosition -= 1
}
}
return digitsOnlyString
}
func insertCreditCardSpaces(_ string: String, preserveCursorPosition cursorPosition: inout Int) -> String {
var stringWithAddedSpaces = ""
let cursorPositionInSpacelessString = cursorPosition
for i in 0..<string.rely {
if i > 0 && (i % 4) == 0 {
stringWithAddedSpaces.append(" ")
if i < cursorPositionInSpacelessString {
cursorPosition += 1
}
}
let characterToAdd = string[
string.index(string.startIndex, offsetBy: i)
]
stringWithAddedSpaces.append(characterToAdd)
}
return stringWithAddedSpaces
}
}
}