Kampf mit NSNumberFormatter in Swift um Währung


86

Ich erstelle eine Budget-App, mit der der Benutzer sowohl sein Budget als auch Transaktionen eingeben kann. Ich muss dem Benutzer erlauben, sowohl Pence als auch Pfund aus separaten Textfeldern einzugeben, und sie müssen zusammen mit Währungssymbolen formatiert werden. Ich habe dies im Moment gut funktionieren, möchte es aber lokalisieren, da es derzeit nur mit GBP funktioniert. Ich habe mich bemüht, NSNumberFormatter-Beispiele von Ziel C bis Swift zu verbergen.

Mein erstes Problem ist die Tatsache, dass ich die Platzhalter für die Eingabefelder so festlegen muss, dass sie spezifisch für den Standort des Benutzers sind. Z.B. Pfund und Pence, Dollar und Cent etc ...

Das zweite Problem besteht darin, dass die in jedes der Textfelder wie 10216 und 32 eingegebenen Werte formatiert und das für den Standort des Benutzers spezifische Währungssymbol hinzugefügt werden muss. Es würde also 10.216,32 £ oder 10.216,32 $ usw. werden.

Außerdem muss ich das Ergebnis der formatierten Zahl in einer Berechnung verwenden. Wie kann ich dies tun, ohne auf Probleme zu stoßen, ohne auf Probleme mit dem Währungssymbol zu stoßen?

Jede Hilfe wäre sehr dankbar.


2
Können Sie ein Beispiel für nicht funktionierenden Code veröffentlichen?
NiñoScript

Antworten:


205

Hier ist ein Beispiel für die Verwendung in Swift 3. ( Bearbeiten : Funktioniert auch in Swift 4)

let price = 123.436 as NSNumber

let formatter = NumberFormatter()
formatter.numberStyle = .currency
// formatter.locale = NSLocale.currentLocale() // This is the default
// In Swift 4, this ^ has been renamed to simply NSLocale.current
formatter.string(from: price) // "$123.44"

formatter.locale = Locale(identifier: "es_CL")
formatter.string(from: price) // $123"

formatter.locale = Locale(identifier: "es_ES")
formatter.string(from: price) // "123,44 €"

Hier ist das alte Beispiel für die Verwendung in Swift 2.

let price = 123.436

let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
// formatter.locale = NSLocale.currentLocale() // This is the default
formatter.stringFromNumber(price) // "$123.44"

formatter.locale = NSLocale(localeIdentifier: "es_CL")
formatter.stringFromNumber(price) // $123"

formatter.locale = NSLocale(localeIdentifier: "es_ES")
formatter.stringFromNumber(price) // "123,44 €"

Vielen Dank. Ich habe meine Frage bearbeitet und war genauer.
user3746428

Anhand des von Ihnen angegebenen Beispiels habe ich es geschafft, die Zahlenformatierung in mein Programm zu implementieren, sodass das Bit sortiert ist. Jetzt muss ich nur noch herausfinden, wie die Platzhalter für Textfelder basierend auf dem Standort des Benutzers festgelegt werden.
user3746428

2
Sie müssen es nicht in NSNumber umwandeln. Sie können die Formatierungsmethode func string (für obj: Any?) -> String? Verwenden. Sie müssen also nur string(for: price)anstelle vonstring(from: price)
Leo Dabus

1
@LeoDabus Sie haben Recht, ich wusste nichts über diese Methode, ich bin mir jedoch nicht sicher, ob ich meine Antwort bearbeiten soll, da ich denke, ich würde lieber die API von NumberFormatter verwenden und explizit über die Verwendung von NSNumber sprechen, anstatt sie implizit zuzulassen Wirf es hinein.
NiñoScript

Beachten Sie, dass das Ergebnis von formatter.string (von :) ein optionaler String ist, kein String (wie in den Kommentaren impliziert), sodass vor der Verwendung ein Entpacken erforderlich ist.
Ali Beadle

25

Swift 3:

Wenn Sie nach einer Lösung suchen, die Ihnen Folgendes bietet:

  • "5" = "$ 5"
  • "5.0" = "$ 5"
  • "5,00" = "$ 5"
  • "5,5" = "5,50 $"
  • "5,50" = "$ 5,50"
  • "5.55" = "$ 5.55"
  • 5.234234 = 5.23

Bitte verwenden Sie Folgendes:

func cleanDollars(_ value: String?) -> String {
    guard value != nil else { return "$0.00" }
    let doubleValue = Double(value!) ?? 0.0
    let formatter = NumberFormatter()
    formatter.currencyCode = "USD"
    formatter.currencySymbol = "$"
    formatter.minimumFractionDigits = (value!.contains(".00")) ? 0 : 2
    formatter.maximumFractionDigits = 2
    formatter.numberStyle = .currencyAccounting
    return formatter.string(from: NSNumber(value: doubleValue)) ?? "$\(doubleValue)"
}

Sie müssen kein neues NSNumber-Objekt initialisieren. Sie können func string(for obj: Any?) -> String?stattdessen die Formatierungsmethode verwendenstring(from:)
Leo Dabus

19

Ich habe die von @ NiñoScript bereitgestellte Lösung auch als Erweiterung implementiert:

Erweiterung

// Create a string with currency formatting based on the device locale
//
extension Float {
    var asLocaleCurrency:String {
        var formatter = NSNumberFormatter()
        formatter.numberStyle = .CurrencyStyle
        formatter.locale = NSLocale.currentLocale()
        return formatter.stringFromNumber(self)!
    }
}

Verwendung:

let amount = 100.07
let amountString = amount.asLocaleCurrency
print(amount.asLocaleCurrency())
// prints: "$100.07"

Swift 3

    extension Float {
    var asLocaleCurrency:String {
        var formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.locale = Locale.current
        return formatter.string(from: self)!
    }
}

Die Erweiterung sollte FloatingPoint für Swift 3-Version und Zeichenfolge erweitern (von: Methode ist für NSNumber. Für FlotingPoint-Typen müssen Sie die Zeichenfolge (für :) -Methode verwenden. Ich habe eine Swift 3-Erweiterung veröffentlicht
Leo Dabus

Verwenden Sie keine Float-Typen für Währungen, sondern Dezimalzahlen.
Adnako

17

Xcode 11 • Swift 5.1

extension Locale {
    static let br = Locale(identifier: "pt_BR")
    static let us = Locale(identifier: "en_US")
    static let uk = Locale(identifier: "en_GB") // ISO Locale
}

extension NumberFormatter {
    convenience init(style: Style, locale: Locale = .current) {
        self.init()
        self.locale = locale
        numberStyle = style
    }
}

extension Formatter {
    static let currency = NumberFormatter(style: .currency)
    static let currencyUS = NumberFormatter(style: .currency, locale: .us)
    static let currencyBR = NumberFormatter(style: .currency, locale: .br)
}

extension Numeric {
    var currency: String { Formatter.currency.string(for: self) ?? "" }
    var currencyUS: String { Formatter.currencyUS.string(for: self) ?? "" }
    var currencyBR: String { Formatter.currencyBR.string(for: self) ?? "" }
}

let price = 1.99

print(Formatter.currency.locale)  // "en_US (current)\n"
print(price.currency)             // "$1.99\n"

Formatter.currency.locale = .br
print(price.currency)  // "R$1,99\n"

Formatter.currency.locale = .uk
print(price.currency)  // "£1.99\n"

print(price.currencyBR)  // "R$1,99\n"
print(price.currencyUS)  // "$1.99\n"

3
Verwenden Sie keine Float-Typen für Währungen, sondern Dezimalzahlen.
Adnako


7

Einzelheiten

  • Xcode 10.2.1 (10E1001), Swift 5

Lösung

import Foundation

class CurrencyFormatter {
    static var outputFormatter = CurrencyFormatter.create()
    class func create(locale: Locale = Locale.current,
                      groupingSeparator: String? = nil,
                      decimalSeparator: String? = nil,
                      style: NumberFormatter.Style = NumberFormatter.Style.currency) -> NumberFormatter {
        let outputFormatter = NumberFormatter()
        outputFormatter.locale = locale
        outputFormatter.decimalSeparator = decimalSeparator ?? locale.decimalSeparator
        outputFormatter.groupingSeparator = groupingSeparator ?? locale.groupingSeparator
        outputFormatter.numberStyle = style
        return outputFormatter
    }
}

extension Numeric {
    func toCurrency(formatter: NumberFormatter = CurrencyFormatter.outputFormatter) -> String? {
        guard let num = self as? NSNumber else { return nil }
        var formatedSting = formatter.string(from: num)
        guard let locale = formatter.locale else { return formatedSting }
        if let separator = formatter.groupingSeparator, let localeValue = locale.groupingSeparator {
            formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
        }
        if let separator = formatter.decimalSeparator, let localeValue = locale.decimalSeparator {
            formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
        }
        return formatedSting
    }
}

Verwendung

let price = 12423.42
print(price.toCurrency() ?? "")

CurrencyFormatter.outputFormatter = CurrencyFormatter.create(style: .currencyISOCode)
print(price.toCurrency() ?? "nil")

CurrencyFormatter.outputFormatter = CurrencyFormatter.create(locale: Locale(identifier: "es_ES"))
print(price.toCurrency() ?? "nil")

CurrencyFormatter.outputFormatter = CurrencyFormatter.create(locale: Locale(identifier: "de_DE"), groupingSeparator: " ", style: .currencyISOCode)
print(price.toCurrency() ?? "nil")

CurrencyFormatter.outputFormatter = CurrencyFormatter.create(groupingSeparator: "_", decimalSeparator: ".", style: .currencyPlural)
print(price.toCurrency() ?? "nil")

let formatter = CurrencyFormatter.create(locale: Locale(identifier: "de_DE"), groupingSeparator: " ", decimalSeparator: ",", style: .currencyPlural)
print(price.toCurrency(formatter: formatter) ?? "nil")

Ergebnisse

$12,423.42
USD12,423.42
12.423,42 €
12 423,42 EUR
12_423.42 US dollars
12 423,42 Euro

3

Aktualisiert für Swift 4 aus der Antwort von @Michael Voccola:

extension Double {
    var asLocaleCurrency: String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.locale = Locale.current

        let formattedString = formatter.string(from: self as NSNumber)
        return formattedString ?? ""
    }
}

Hinweis: Keine Force-Unwraps, Force-Unwraps sind böse.


2

Swift 4 TextField implementiert

var value = 0    
currencyTextField.delegate = self

func numberFormatting(money: Int) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.locale = .current
        return formatter.string(from: money as NSNumber)!
    }

currencyTextField.text = formatter.string(from: 50 as NSNumber)!

func textFieldDidEndEditing(_ textField: UITextField) {
    value = textField.text
    textField.text = numberFormatting(money: Int(textField.text!) ?? 0 as! Int)
}

func textFieldDidBeginEditing(_ textField: UITextField) {
    textField.text = value
}

0
extension Float {
    var convertAsLocaleCurrency :String {
        var formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.locale = Locale.current
        return formatter.string(from: self as NSNumber)!
    }
}

Dies funktioniert für Swift 3.1 Xcode 8.2.1


Während dieser Code - Schnipsel zu begrüßen ist, und etwas Hilfe bieten kann, würde es erheblich verbessert , wenn es eine Erklärung enthalten von , wie und warum Dies löst das Problem. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt! Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, und geben Sie an, welche Einschränkungen und Annahmen gelten.
Toby Speight

Verwenden Sie keine Float-Typen für Währungen, sondern Dezimalzahlen.
Adnako

0

Swift 4

formatter.locale = Locale.current

Wenn Sie das Gebietsschema ändern möchten, können Sie dies folgendermaßen tun

formatter.locale = Locale.init(identifier: "id-ID") 

// Dies ist das Gebietsschema für das Gebietsschema Indonesien. Wenn Sie die Verwendung gemäß dem Mobilfunkbereich verwenden möchten, verwenden Sie diese gemäß der oben genannten Erwähnung Locale.current

//MARK:- Complete code
let formatter = NumberFormatter()
formatter.numberStyle = .currency
    if let formattedTipAmount = formatter.string(from: Int(newString)! as 
NSNumber) { 
       yourtextfield.text = formattedTipAmount
}

0

füge diese Funktion hinzu

func addSeparateMarkForNumber(int: Int) -> String {
var string = ""
let formatter = NumberFormatter()
formatter.locale = Locale.current
formatter.numberStyle = .decimal
if let formattedTipAmount = formatter.string(from: int as NSNumber) {
    string = formattedTipAmount
}
return string
}

mit:

let giaTri = value as! Int
myGuessTotalCorrect = addSeparateMarkForNumber(int: giaTri)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.