0

I have a UITextField: its text is a longitude (double) value. My device prints this double with a dot, eg 24.000000 (and not 24,00000).

That said, the textfield should not accept invalid values: to do that, I use the decimalPad keyboard, that contains numbers and a decimal sign (in my case, the sign is , , NOT . but it looks like this varies based on locales).

When the user edits the textfield, the double value is passed to a MapKit map, so that value must be valid before I pass it.

The validity of a latitude value is quite clear (value>-90 and value<90), but I don't know how to convert the string to a valid double.

First of all, in my case, the initial data come from:

mapView.convert(touchPoint, toCoordinateFrom: mapView)

This one returns coordinates with a dot and displays that coordinate in the textfield.

Second: if the user is editing the textfield, he can delete a dot but he can't insert it anymore. Its dot should be replaced by a comma because in the decimal pad I only have a comma.

Third: I tried this extension:

extension String {
    struct NumFormatter {
        static let instance = NumberFormatter()
    }

    var doubleValue: Double? {
        return NumFormatter.instance.number(from: self)?.doubleValue
    }
}

to test if the value in the textfield is a double or not. If I insert a comma, it considers the value a double. If I insert a dot, the value is nil.

How do I validate my longitude value?

  • 1
    try this: `if let text = textField.text, let yourDouble = Double(text.replacingOccurrences(of: ",", with: ".")) { print(yourDouble) }` – mugx Jan 26 '18 at 10:57
  • @AndreaMugnaini : of course this works, but is there any way to know which decimal separator will use the keyboard? This way I could have only commas or only dots. It's strange to display a dot and make the user replace it with a comma –  Jan 26 '18 at 11:32
  • 1
    if you want to have more explanations, you may check here: https://stackoverflow.com/q/30150829/2450755, you may like this answer: https://stackoverflow.com/a/44595112/2450755 – mugx Jan 26 '18 at 11:47
  • What if user enters more than one comma in textfield.for example if user enters "123,4,5,6" do you want to consider it as "123.456" – Vikky Jan 26 '18 at 11:59
  • @Vikky: it they enter more than a comma, the value is not valid and I consider it not valid at all –  Jan 26 '18 at 12:23
  • @AndreaMugnaini: ok, thank you, I had previously found that question but I had not red that answer –  Jan 26 '18 at 12:25
  • 1
    @3000 No worry, you are welcome. – mugx Jan 26 '18 at 12:26
  • 1
    `CLLocationCoordinate2DIsValid ` Returns a Boolean value indicating whether the specified coordinate is valid. – Kosuke Ogawa Jan 26 '18 at 12:28
  • @KosukeOgawa: very interesting, thanks –  Jan 26 '18 at 12:43

1 Answers1

0

I wrote this function for myself to validate an input when the textfield's didBeginEditing delegate method is fired. This function tries to generate a valid Double number from a String or will return a default value.

After editing is Done, the didFinishEditing delegate method can make sure the number doesn't end with a decimal point character so that the output of "10." would be "10" but Im handling that part differently for myself.

You will need to change the function or the decimal point character or the number of decimal places for your own purpose. In my case every time the value in the textfield is changed, the text will be validated and manipulated to make sure user can not enter an invalid input while typing in the textfield is happening.

private func validate(from text: String?) -> String {
    guard var text = text else { return "1.0" }  // makes sure the textfield.text is not nil
    guard text.first != "." else { return "0." } // prevents entering ".0"

    text = text.filter{ CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: $0.description)) || $0 == "." } // makes sure the entered text is only digits or "." otherwise removes the non digit characters
    let integerPart = String(text[text.startIndex..<(text.index(of: ".") ?? text.endIndex)]) // it is the digits before the first decimal point
    var decimalPlaces = "" // digits after the decimal point
    var afterIntegerText = "" // the text after the integer number before the decimal point
    if let decimalPointIndex = text.index(of: ".") { // if the number has a decimal point
        afterIntegerText = String(text[decimalPointIndex..<text.endIndex]) // "10.12345 will result become ".12345"
        decimalPlaces = String(afterIntegerText.replacingOccurrences(of: ".", with: "").prefix(2)) // ".12345" will become "12"
        decimalPlaces = afterIntegerText.isEmpty ? "" : ".\(decimalPlaces)" // generates the text of decimal place character and the numbers if any
    }
    return "\(integerPart + decimalPlaces)" // "10.*$1.2." will become "10.12"
}
Ramin
  • 335
  • 4
  • 14