Make iOS SwiftUI TextField reject bad numeric input

Viewed 353

My iOS SwiftUI TextField allows the user to input an Int. If the user types bad input characters such as "abc" instead of "123", I would like to display the bad characters in an error message in my Text (where I wrote textField.text), and I would like to keep the TextField's keyboard on the screen until the user provides correct input. How to make this happen? Thank you in advance. Xcode 11.6 on macOS Catalina 10.15.6.

struct ContentView: View {
    @State private var value: Int? = nil;
    @State private var text: String = "";

    var body: some View {
        VStack {
            TextField(
                "Enter an integer:",
                value: $value,
                formatter: NumberFormatter(),
                onCommit: {
                    guard let value: Int = self.value else {
                        self.text = "Bad input \"\(textField.text)\".";
                        //Do not dismiss the TextField's keyboard.
                        return;
                    }
                    //Dismiss the TextField's keyboard.
                    self.text = "The product is \(2 * value).";
                }
            )
            .keyboardType(.numbersAndPunctuation)
            .textFieldStyle(RoundedBorderTextFieldStyle())

            Text(text)
        }
        .padding([.leading, .trailing], 16)
    }
}

2 Answers

Below code gives you an idea for validating your text inside the textfield while user is typing.

struct ContentView: View {

@State private var textNumber    : String = ""
@State private var isNumberValid : Bool   = true

var body: some View {
    VStack {
        
        TextField("Enter an integer:", text: numberValidator())
            .keyboardType(.numbersAndPunctuation)
            .textFieldStyle(RoundedBorderTextFieldStyle())
        
        if !self.isNumberValid {
            Text("Bad input \"\(textNumber)\".")
                .font(.callout)
                .foregroundColor(Color.red)
        }
    }
    .padding([.leading, .trailing], 16)
}

private func numberValidator() -> Binding<String> {
    return Binding<String>(
        get: {
            return self.textNumber
    }) {
        if CharacterSet(charactersIn: "1234567890").isSuperset(of: CharacterSet(charactersIn: $0)) {
            self.textNumber = $0
            self.isNumberValid = true
        } else {
            self.textNumber = $0
            //self.textNumber = ""
            self.isNumberValid = false
        }
    }
  }
}

The following code snippet gives you an idea to validate your text inside the text field as the user is typing using Get, Set value

    let checkValue = Binding<String>(
        get: {
            self.value
        },
        set: {
            self.value = $0
        }
    )

I hope it can be of help to you

struct ContentView: View {
    @State private var value: String = ""
    @State private var text: String = ""

    var body: some View {
        let checkValue = Binding<String>(
            get: {
               self.value
            },
            set: {
                self.value = $0
            }
        )

        return VStack {
            TextField("Enter an integer:",text: checkValue)
            .keyboardType(.numbersAndPunctuation)
            .textFieldStyle(RoundedBorderTextFieldStyle())

            Text("Bad interger: \(Int(self.value) != nil ? "" : self.value)").foregroundColor(Color.red)
        
        }
        .padding([.leading, .trailing], 16)
    }
}
Related