13

I'm using a textField which is filled from a numerical pad.

Trouble is that, with lot of local region formats (all european, for example), UITextField's numerical pad has comma instead dot, so everytime I write a decimal number, UITextField can't recognise the decimal comma and it round number; for example 23,07 become 23.

How can I solve this? I thought to set the textField fixed on USA; is it possible? How?

I read the value using this:

var importo = (importoPrevistoTF.text as NSString).floatValue
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Matte.Car
  • 2,007
  • 4
  • 23
  • 41
  • Show your code. More countries use a decimal comma. than a decimal point, see: [Decimal mark](http://en.wikipedia.org/wiki/Decimal_mark). Oh, I can't believe the USA still does not use the Metric system of units! ;-) – zaph May 10 '15 at 11:48
  • @zaph, collectively, we're idiots. :) We teach the metric system half-heartedly in school. Canada did it right. Starting in the early 70s, they simply stopped teaching kids the "imperial" system and switched. – Duncan C May 10 '15 at 11:53
  • @DuncanC We are not alone, Liberia and Myanmar also don't use the metric system, that's it, just the three of us. – zaph May 10 '15 at 11:57
  • 1
    Added code in question. I think metric system is smarter, I just can't understand why don't find a compromise to use comma or dot for everybody... – Matte.Car May 10 '15 at 12:01
  • It would be a duplicate if it was in Swift... – Matte.Car May 10 '15 at 12:15

11 Answers11

24

Swift 4

extension String {
    static let numberFormatter = NumberFormatter()
    var doubleValue: Double {
        String.numberFormatter.decimalSeparator = "."
        if let result =  String.numberFormatter.number(from: self) {
            return result.doubleValue
        } else {
            String.numberFormatter.decimalSeparator = ","
            if let result = String.numberFormatter.number(from: self) {
                return result.doubleValue
            }
        }
        return 0
    }
}

"2.25".doubleValue // 2.25
"2,25".doubleValue // 2.25


Localized approach using NumberFormatter:

extension NumberFormatter {
    static let shared = NumberFormatter()
}
extension StringProtocol {
    var doubleValue: Double? {
        return NumberFormatter.shared.number(from: String(self))?.doubleValue
    }
}

Playground testing

// User device's default settings for current locale (en_US)
NumberFormatter.shared.locale            // en_US (current)
NumberFormatter.shared.numberStyle       // none
NumberFormatter.shared.decimalSeparator  // "."
"2.7".doubleValue  // 2.7
"2,7".doubleValue  // nil
"$2.70".doubleValue  // nil

NumberFormatter.shared.numberStyle  = .currency
"2.7".doubleValue  // nil
"2,7".doubleValue  // nil
"$2.70".doubleValue  // 2.7


NumberFormatter.shared.locale  = Locale(identifier: "pt_BR") // pt_BR (fixed)
"2.7".doubleValue     // nil
"2,7".doubleValue     // nil
"R$2,70".doubleValue  // 2.7

NumberFormatter.shared.numberStyle = .none
"2.7".doubleValue      // nil
"2,7".doubleValue      // 2.7
"R$2,70".doubleValue   // nil
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • 1
    I admit that I didn't saw this question before...but this solution is much better! – Matte.Car May 10 '15 at 19:23
  • This is a suboptimal solution. See https://stackoverflow.com/a/30152398/337934 or https://stackoverflow.com/a/30151275/337934 which don't assume localization – SamB Feb 06 '19 at 03:40
15

Potential duplicate of the SO Answer, use NSNumberFormatter

Example Swift:

let number = NSNumberFormatter().numberFromString(numberString)
if let number = number {
    let floatValue = Float(number)
}

Example (Objective-C):

NSNumber *number = [[NSNumberFormatter new] numberFromString: numberString];
float floatValue = number.floatValue;
Community
  • 1
  • 1
zaph
  • 111,848
  • 21
  • 189
  • 228
  • There's a strange problem. With some regional formats, for example italian, dot and comma works, but with other formats, for example USA, dot works but comma crash; how can it be possible? – Matte.Car Oct 12 '15 at 19:41
12

Nobody has really addressed the issue directly.

That is, the decimal separator is a convention for a locale.

iOS supports formatting numbers based on a particular locale.

If you're working purely in a given locale, then everything should work correctly. The keypad should accept numbers with the correct decimal separator.

If you're in most countries in Europe, for example, you'd enter a comma as the decimal separator. Entering a dot in those countries is wrong. Somebody from one of those countries would not do that, because it is the wrong decimal separator. A European user is going to know to use a comma as the decimal separator and you don't have to do anything.

If you are in the US, you'd use a period. Using a comma in the US would be wrong.

The way you should display a decimal number is with a number formatter. When you create a number formatter, it uses the current locale by default.

If you need to convert a string containing a decimal number from one locale to the other, you should use 2 number formatters. Use a formatter in the source locale to convert the string to a float. Then use a formatter with the destination locale to convert the number to a string in the output format.

Simply create one number formatter in the default current locale, and create a second number formatter and set it's locale explicitly to the other locale that you want to use.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
5

It's probably a duplicate of this answer, but since the original is in Objective-C, here's a Swift version:

let label = "23,07"
let formatter = NSNumberFormatter()
let maybeNumber = formatter.numberFromString(label)
if let number = maybeNumber {
    println(number)   // 23.07
}
Community
  • 1
  • 1
Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • Thank you very much! I had just finished translating in Swift then I saw you answer! – Matte.Car May 10 '15 at 12:33
  • 2
    The problem is that `formatter.numberFromString(label)` may return `nil` and if the locale does not match the string format it will return nil. – zaph May 10 '15 at 12:39
2

Swift 3: float or double value for string containing floating point with comma

extension String {
    var floatValue: Float {
        let nf = NumberFormatter()
        nf.decimalSeparator = "."
        if let result = nf.number(from: self) {
            return result.floatValue
        } else {
            nf.decimalSeparator = ","
            if let result = nf.number(from: self) {
                return result.floatValue
            }
        }
        return 0
    }

    var doubleValue:Double {
        let nf = NumberFormatter()
        nf.decimalSeparator = "."
        if let result = nf.number(from: self) {
            return result.doubleValue
        } else {
            nf.decimalSeparator = ","
            if let result = nf.number(from: self) {
                return result.doubleValue
            }
        }
        return 0
    }
}

Example:

"5,456".floatValue //5.456
"5.456".floatValue //5.456
"5,456".doubleValue //5.456
"5.456".doubleValue //5.456

"5,456".doubleValue.rounded() //5
"5,6".doubleValue.rounded() //6
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
1

Since NSNumberFormatter was replaced by NumberFormatter in the recent version of Swift, I would have pleasure to share with you an upgraded possible solution:

var numberFormatter: NumberFormatter()
importo = Float(numberFormatter.number(from: importoPrevistoTF.text!)!)
giudigior
  • 146
  • 6
1

A solution that i've found:

let nf = NumberFormatter()
nf.locale = Locale.current
let numberLocalized = nf.number(from: txtAlcool.text!)

In my case I was testing on xcode and all goes ok, but when testing on device it was crashing. All because in Brazil we use metric system, comma separated decimal ",". With this solution it converts automatically from comma to dot.

1

Code working with the current version of Swift:

let amount = "8,35"

var counter: Int = 0
var noCommaNumber: String!
for var carattere in (amount) {
    if carattere == "," { carattere = "." }
    if counter != 0 { noCommaNumber = "\(noCommaNumber ?? "\(carattere)")" + "\(carattere)" } else { noCommaNumber = "\(carattere)" } // otherwise first record will always be nil
    counter += 1
}

let importo = Float(noCommaNumber)
Matte.Car
  • 2,007
  • 4
  • 23
  • 41
0

Swift 4 solution, without using preferredLanguages I had issues with fr_US and decimalPad

extension String {

     func number(style: NumberFormatter.Style = .decimal) -> NSNumber? {
        return [[Locale.current], Locale.preferredLanguages.map { Locale(identifier: $0) }]
            .flatMap { $0 }
            .map { locale -> NSNumber? in
                let formatter = NumberFormatter()
                formatter.numberStyle = style
                formatter.locale = locale
                return formatter.number(from: self)
        }.filter { $0 != nil }
        .map { $0! }
        .first
    }
}

textfield.text?.number()?.floatValue
Arnaud Dorgans
  • 363
  • 1
  • 2
  • 14
0

You can convert it by using NumberFormatter and filtering the different decimal separators:

func getDoubleFromLocalNumber(input: String) -> Double {
    var value = 0.0
    let numberFormatter = NumberFormatter()
    let decimalFiltered = input.replacingOccurrences(of: "٫|,", with: ".", options: .regularExpression)
    numberFormatter.locale = Locale(identifier: "EN")
    if let amountValue = numberFormatter.number(from: decimalFiltered) {
        value = amountValue.doubleValue
    }
    return value
}
Soheil Novinfard
  • 1,358
  • 1
  • 16
  • 43
-1
        let number                      =   NSNumberFormatter()
        let locale                      =   NSLocale.currentLocale()
        let decimalCode                 =   locale.objectForKey(NSLocaleDecimalSeparator) as! NSString

        number.decimalSeparator = decimalCode as String
        let result = number.numberFromString(textField.text!)

        let value = NSNumberFormatter.localizedStringFromNumber(result!.floatValue, numberStyle: .DecimalStyle)
        print(value)

Hope, this helps you :)

xcodeUSER
  • 1
  • 1