0

The following code effectively limits a TextFild to only numbers, periods . and commas , which is what I want. The issue is that it allows the user to enter a period . and a comma , if a period is entered first.

How can I modify the Regex expression below to prevent the user from entering a comma , if a period . already exists, and vice-versa?

struct TextFieldNumbersOnly: View {
    @State private var inputValue = ""

    var body: some View {
        TextField("enter numbers", text: self.$inputValue)
            //.keyboardType(.decimalPad) //commented out for testing purposes only
            .onReceive(Just(self.inputValue), perform: self.textInputValidator)
    }
    
    func textInputValidator(newValue: String) {
        if newValue.range(of: "^[\\d]*(?:\\.?\\,?[\\d]*)?$", options: .regularExpression) != nil {
            self.inputValue = newValue
        } else if !self.inputValue.isEmpty {
            self.inputValue = String(newValue.prefix(self.inputValue.count - 1))
        }
    }
}

FYI - If I type a comma before a period, it doesn't let me enter the period, which is what I want, it just doesn't do it the other way around.

Ken White
  • 123,280
  • 14
  • 225
  • 444
fs_tigre
  • 10,650
  • 13
  • 73
  • 146
  • 1
    Better to allow only digits and you apply the formatting using number formatter. Possible duplicate of [How to input currency format on a text field (from right to left) using Swift?](https://stackoverflow.com/a/29783546/2303865) – Leo Dabus Dec 31 '22 at 04:42
  • 1
    Another option is to use an init that takes a value and a format style or formatter instead and let the framework do the work for you. It might not be perfect but it requires no custom code. – Joakim Danielson Dec 31 '22 at 08:28
  • @LeoDabus - What I'm trying to do is validate a field that takes a percentage amount. A few months back I actually tried the `SwiftUI` version of your `CurrencyField` implementation but back then you recommended using the `UIKit` version instead, is that still the case? Has the `SwiftUI` version been tested in production? Which version do you recommend at this point? Back then I also tried to wrap the `UIKit` version in a `UIViewRepresentable` but I ran into issues. Maybe it's time to revisit this and try to use your implementation for my currency fields. – fs_tigre Dec 31 '22 at 16:55

1 Answers1

2

Try this:

^\d+[.,]?\d*$|^[.,]\d*$

^\d+[.,]?\d*$

  • ^ match the start of the string/line.
  • \d+ match one or more digit.
  • [.,]? match an optional literal . or ,.
  • \d* match zero or more digits.
  • $ match the end of the string/line.

| the alternation operator it is like Boolean OR

^[.,]\d*$

  • ^ match the start of the string/line.
  • [,.] match one literal . or ,.
  • \d* match zero or more digits.
  • $ match the end of the string/line.

See regex demo

SaSkY
  • 1,086
  • 1
  • 4
  • 14
  • For some reason, I had to add double backslashes to the `d`s as follows, `"^\\d+[.,]?\\d*$|^[.,]\\d*$"` and it worked as expected. I was getting error, `Missing argument for parameter 'of' in call`. Thanks a lot. – fs_tigre Dec 31 '22 at 13:07
  • 1
    @fs_tigre you need the double backslash because it is the string escape character. You can fix this in swift by using `#"...."#` and then the string escape sequence becomes `\#`. This is very useful when creating regexes as strings.. In your case: `#"^\d+[.,]?\d*$|^[.,]\d*$"#`. It also allows you to embed unescaped `"` in strings. – JeremyP Dec 31 '22 at 16:13