在Swift中与NSNumberFormatter争夺货币


86

我正在创建一个预算应用程序,允许用户输入他们的预算以及交易。我需要允许用户从单独的文本字段中输入便士和英镑,并且它们需要与货币符号一起设置格式。我目前可以正常使用,但想将其本地化,因为目前仅适用于GBP。我一直在努力将NSNumberFormatter示例从Objective C转移到Swift。

我的第一个问题是我需要为输入字段设置占位符以使其特定于用户位置。例如。磅,便士,美元和美分等...

第二个问题是,需要格式化在每个文本字段(例如10216和32)中输入的值,并且需要添加特定于用户位置的货币符号。因此它将变成10,216.32英镑或10,216.32美元,等等...

另外,我需要在计算中使用格式化数字的结果。那么,如何在不遇到货币符号问题的情况下做到这一点呢?

任何帮助将非常感激。


2
您可以发布非工作代码示例吗?
NiñoScript

Answers:


205

这是一个有关如何在Swift 3上使用它的示例。(编辑:在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 €"

这是关于如何在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 €"

谢谢。我已经编辑了我的问题,并且更加具体。
user3746428 2014年

根据您提供的示例,我设法在程序中实现了数字格式化,以便对位进行排序。现在,我只需要弄清楚如何根据用户位置设置文本字段占位符。
user3746428 2014年

2
无需将其强制转换为NSNumber即可使用格式化程序方法func string(对于obj:Any?)-> String?。所以,你只需要使用string(for: price)替代string(from: price)
狮子座Dabus

1
@LeoDabus你是对的,我不知道那种方法,但是我不确定是否应该编辑我的答案,因为我想我宁愿使用NumberFormatter的API并明确使用NSNumber而不是隐式地使用它放进去
NiñoScript

请注意,formatter.string(from :)的结果是一个可选的String,而不是一个String(如注释所暗示的),因此在使用前需要解开包装。
Ali Beadle

25

斯威夫特3:

如果您正在寻找一种可以为您提供解决方案的解决方案:

  • “ 5” =“ $ 5”
  • “ 5.0” =“ $ 5”
  • “ 5.00” =“ $ 5”
  • “ 5.5” =“ $ 5.50”
  • “ 5.50” =“ $ 5.50”
  • “ 5.55” =“ $ 5.55”
  • “ 5.234234” =“ 5.23”

请使用以下内容:

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

无需初始化新的NSNumber对象,您可以使用格式化程序方法func string(for obj: Any?) -> String?代替string(from:)
Leo Dabus

19

我还实现了@NiñoScript提供的解决方案作为扩展:

延期

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

用法:

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

迅捷3

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

扩展名应该扩展为Swift 3版本的FloatingPoint,而string(from:方法是用于NSNumber。对于FlotingPoint类型,您需要使用string(for :)方法。我发布了一个Swift 3扩展名
Leo Dabus

不要将浮点类型用于货币,请使用十进制。
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
不要将浮点类型用于货币,请使用十进制。
adnako


7

细节

  • Xcode 10.2.1(10E1001),Swift 5

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

用法

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

结果

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

3

已从@Michael Voccola的答案为Swift 4更新:

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

注意:没有强行包装,强行包装是邪恶的。


2

Swift 4 TextField实现

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

这适用于Swift 3.1 xcode 8.2.1


尽管此代码段是受欢迎的,并且可能会提供一些帮助,但是如果它包含有关如何以及为什么可以解决此问题的说明,则可以大大改善。请记住,您将来会为读者回答问题,而不仅仅是现在问的人!请编辑您的答案以添加解释,并指出适用的限制和假设。
Toby Speight

不要将浮点类型用于货币,请使用十进制。
adnako

0

斯威夫特4

formatter.locale = Locale.current

如果您想更改区域设置,可以这样做

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

//这是印度尼西亚语言环境的语言环境。如果您想按手机区域使用,请按上方提及使用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

添加此功能

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
}

使用:

let giaTri = value as! Int
myGuessTotalCorrect = addSeparateMarkForNumber(int: giaTri)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.