SwiftUI Formatting TextField for credit card input "xxxx-xxxx-xxxx-xxxx" – Swift

by
Ali Hasan
axios swift-concurrency swiftui-animation uitextfield uiviewrepresentable

Quick Fix: Implement Coordinator with parent parameter (your CreditCardTextField) and implement UITextFieldDelegate method textViewDidChange. Do all manipulations with string there and apply result to parent.number

The Problem:

You want to write a custom SwiftUI TextField that automatically formats user input as a credit card number in the format xxxx-xxxx-xxxx-xxxx. You’re unsure how to update the original text field value with the formatted string.

The Solutions:

Solution 2: Typing in Credit Card Numbers

To format the TextField input into credit card format ("xxxx-xxxx-xxxx-xxxx"), we can implement a custom CreditCardTextField using UIViewRepresentable. Here’s how:

1. Formatting Helper Method:

private static func formatCreditCardNumber(_ number: String) -> String {
    let formattedNumber = number
        .components(separatedBy: CharacterSet.decimalDigits.inverted)
        .joined()
        .replacingOccurrences(of: "(.{4})", with: "$1 ", options: .regularExpression, range: nil)
        .trimmingCharacters(in: .whitespacesAndNewlines)
    return formattedNumber
}

2. CreditCardTextField:

struct CreditCardTextField: UIViewRepresentable {
    @Binding var number: String
    private var didChange: ((String) -> Void)?

    init(number: Binding<String>, didChange: @escaping (String) -> Void = { _ in }) {
        self._number = number
        self.didChange = didChange
    }

    func makeUIView(context: Context) -> UITextField {
        let textField = UITextField()
        textField.keyboardType = .numberPad
        textField.addTarget(context.coordinator, action: #selector(Coordinator.textChanged), for: .editingChanged)
        return textField
    }

    func updateUIView(_ textField: UITextField, context: Context) {
        // Avoid infinite loop
        if textField.text == number { return }

        textField.text = CreditCardTextField.formatCreditCardNumber(number)
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: CreditCardTextField

        init(_ parent: CreditCardTextField) {
            self.parent = parent
        }

        @objc func textChanged(sender: UITextField) {
            parent.number = sender.text ?? ""
            parent.didChange?(parent.number)
        }
    }
}

3. Usage:

In your ContentView, you can use the CreditCardTextField like this:

struct ContentView: View {
    @State var cardNumber = ""

    var body: some View {
        VStack {
            CreditCardTextField(number: $cardNumber)
                .frame(height: 50)
                .border(.black)
        }
        .padding()
    }
}

This solution provides a more streamlined and efficient way to format credit card input. It separates the formatting logic into a static method and eliminates the need for a Coordinator, resulting in a simpler and more maintainable implementation.

Q&A

How do you apply formats to the Text Field and write the updated code ?

Set up a method for formatting the text that takes a string and transform the string, so that the formatting could be static and reused at different places if needed.

How to give an update to the UIView?

Update the UI view it the TextField already has a formatted number and if it doesn’t format the number and set value to it’s binding.

Video Explanation:

The following video, titled "SwiftUI Credit/Debit Card Input Form - Move to Next TextField", provides additional insights and in-depth exploration related to the topics discussed in this post.

Play video

Hello Guys In this video, I'm going to show how to create a credit/debit card input in a stylish manner with keyboard binding via ...