Lottando con NSNumberFormatter in Swift per la valuta


87

Sto creando un'app per il budget che consente all'utente di inserire il proprio budget e le transazioni. Devo consentire all'utente di inserire sia penny che sterline da campi di testo separati e devono essere formattati insieme ai simboli di valuta. Al momento funziona bene, ma vorrei renderlo localizzato poiché attualmente funziona solo con GBP. Ho lottato per nascondere esempi di NSNumberFormatter da Objective C a Swift.

Il mio primo problema è il fatto che devo impostare i segnaposto per i campi di input in modo che siano specifici per la posizione degli utenti. Per esempio. Sterline e Pence, Dollari e Centesimi ecc ...

Il secondo problema è che i valori immessi in ciascuno dei campi di testo come 10216 e 32 devono essere formattati e deve essere aggiunto il simbolo di valuta specifico per la posizione dell'utente. Quindi diventerebbe £ 10.216,32 o $ 10.216,32 ecc ...

Inoltre, ho bisogno di utilizzare il risultato del numero formattato in un calcolo. Quindi come posso farlo senza incorrere in problemi senza incorrere in problemi con il simbolo della valuta?

Qualsiasi aiuto sarebbe molto apprezzato.


2
puoi postare un esempio di codice non funzionante?
NiñoScript

Risposte:


207

Ecco un esempio su come usarlo su Swift 3. ( Modifica : funziona anche 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 €"

Ecco il vecchio esempio su come usarlo su 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 €"

Grazie. Ho modificato la mia domanda e sono stato più specifico.
user3746428

Sulla base dell'esempio che hai fornito, sono riuscito a implementare la formattazione del numero nel mio programma, in modo che quel bit sia ordinato. Ora ho solo bisogno di capire come impostare i segnaposto del campo di testo in base alla posizione degli utenti.
user3746428

2
Non è necessario eseguirne il cast su NSNumber, è possibile utilizzare il metodo del formattatore func string (per obj: Any?) -> String ?. Quindi devi solo usare al string(for: price)posto distring(from: price)
Leo Dabus

1
@LeoDabus hai ragione, non sapevo di quel metodo, non sono sicuro se dovrei modificare la mia risposta, perché penso che preferirei usare l'API di NumberFormatter ed essere esplicito sull'uso di NSNumber piuttosto che lasciarlo implicitamente gettalo dentro.
NiñoScript

Si noti che il risultato di formatter.string (da :) è una stringa opzionale e non una stringa (come implicito nei commenti) quindi sarà necessario scartare prima dell'uso.
Ali Beadle

25

Swift 3:

Se stai cercando una soluzione che ti dia:

  • "5" = "$ 5"
  • "5.0" = "$ 5"
  • "5.00" = "$ 5"
  • "5.5" = "$ 5,50"
  • "5.50" = "$ 5,50"
  • "5,55" = "$ 5,55"
  • "5.234234" = "5,23"

Si prega di utilizzare quanto segue:

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)"
}

Non è necessario inizializzare un nuovo oggetto NSNumber che è possibile utilizzare al func string(for obj: Any?) -> String?posto del metodo di formattazionestring(from:)
Leo Dabus

19

Ho implementato anche la soluzione fornita da @ NiñoScript come estensione:

Estensione

// 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)!
    }
}

Utilizzo:

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)!
    }
}

l'estensione dovrebbe estendere FloatingPoint per la versione Swift 3 e la stringa (da: il metodo è per NSNumber. Per i tipi FlotingPoint devi usare il metodo stringa (per :). Ho pubblicato un'estensione Swift 3
Leo Dabus

Non utilizzare tipi float per la valuta, utilizzare decimal.
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
Non utilizzare tipi float per la valuta, utilizzare decimal.
adnako


7

Dettagli

  • Xcode 10.2.1 (10E1001), Swift 5

Soluzione

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
    }
}

Utilizzo

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")

Risultati

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

3

Aggiornato per Swift 4 dalla risposta di @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 ?? ""
    }
}

Nota: nessuno scartare forzatamente, scartare forzatamente è il male.


2

Swift 4 TextField implementato

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)!
    }
}

Funziona con swift 3.1 xcode 8.2.1


Sebbene questo snippet di codice sia il benvenuto e possa fornire un aiuto, sarebbe notevolmente migliorato se includesse una spiegazione di come e perché questo risolve il problema. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che lo chiede ora! Si prega di modificare la risposta di aggiungere una spiegazione, e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Toby Speight

Non utilizzare tipi float per la valuta, utilizzare decimal.
adnako

0

Swift 4

formatter.locale = Locale.current

se vuoi cambiare locale puoi farlo in questo modo

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

// Questa è la locale per l'Indonesia. se si desidera utilizzare secondo l'area del telefono cellulare, utilizzarlo come da menzione superiore 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

aggiungi questa funzione

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
}

utilizzando:

let giaTri = value as! Int
myGuessTotalCorrect = addSeparateMarkForNumber(int: giaTri)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.