如何将Double格式化为货币——Swift 3

89

我刚接触Swift编程,正在Xcode 8.2中创建一个简单的小费计算器应用程序,我的计算设置在下面的IBAction中。但是,当我实际运行应用程序并输入要计算的金额(例如23.45),它会显示超过2个小数位。在这种情况下,如何将其格式化为.currency

@IBAction func calculateButtonTapped(_ sender: Any) {

    var tipPercentage: Double {

        if tipAmountSegmentedControl.selectedSegmentIndex == 0 {
            return 0.05
        } else if tipAmountSegmentedControl.selectedSegmentIndex == 1 {
            return 0.10
        } else {
            return 0.2
        }
    }

    let billAmount: Double? = Double(userInputTextField.text!)

    if let billAmount = billAmount {
        let tipAmount = billAmount * tipPercentage
        let totalBillAmount = billAmount + tipAmount

        tipAmountLabel.text = "Tip Amount: $\(tipAmount)"
        totalBillAmountLabel.text = "Total Bill Amount: $\(totalBillAmount)"
    }
}
12个回答

156
您可以使用此字符串初始化程序,如果您想强制货币为 $ :
String(format: "Tip Amount: $%.02f", tipAmount)
如果您希望货币符号和小数位数根据设备的语言环境进行全面调整,应使用 NumberFormatter。例如,对于 es_ES 语言环境,双精度值 2.4 将返回 "2,40 €",在 jp_JP 语言环境下将返回 "¥ 2"。
let formatter = NumberFormatter()
formatter.locale = Locale.current // Change this to another locale if you want to force a specific locale, otherwise this is redundant as the current locale is the default already
formatter.numberStyle = .currency
if let formattedTipAmount = formatter.string(from: tipAmount as NSNumber) {
    tipAmountLabel.text = "Tip Amount: \(formattedTipAmount)"
}

2
请注意 - 无需将格式化程序的 locale 设置为 Locale.current,因为这是默认设置。 - rmaddy
22
是的,我知道,我已经添加了一条评论来表明它是冗余的。想保留它以展示它可以很容易地被改变。 - silicon_valley

46

如何在Swift 4中实现:

let myDouble = 9999.99
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
// localize to your grouping and decimal separator
currencyFormatter.locale = Locale.current

// We'll force unwrap with the !, if you've got defined data you may need more error checking

let priceString = currencyFormatter.string(from: NSNumber(value: myDouble))!
print(priceString) // Displays $9,999.99 in the US locale

26

从Swift 5.5开始,您可以借助.formatted来实现此操作:

import Foundation

let amount = 12345678.9
print(amount.formatted(.currency(code: "USD")))
// prints: $12,345,678.90

这将支持大多数常见的货币代码,例如 "EUR", "GBP" 或 "CNY"。

同样地,您可以将区域设置附加到 .currency

print(amount.formatted(
    .currency(code:"EUR").locale(Locale(identifier: "fr-FR"))
))
// prints: 12 345 678,90 €

2
这个解决方案比所有其他答案都简单得多!我简直不敢相信我直到现在才知道它! - Gavin Morrow
2
非常好,但仅适用于iOS 15。 - Riddik
TIL。谢谢!这是一个非常有用的命令,我以前从未意识到它的存在。 - undefined

24

您可以这样转换:此函数将为您保留 maximumFractionDigits,无论何时您想要进行转换

static func df2so(_ price: Double) -> String{
        let numberFormatter = NumberFormatter()
        numberFormatter.groupingSeparator = ","
        numberFormatter.groupingSize = 3
        numberFormatter.usesGroupingSeparator = true
        numberFormatter.decimalSeparator = "."
        numberFormatter.numberStyle = .decimal
        numberFormatter.maximumFractionDigits = 2
        return numberFormatter.string(from: price as NSNumber)!
    } 

我在模型类中创建了它, 然后当你调用时,你可以在另一个类中访问它,就像这样:

 print("InitData: result convert string " + Model.df2so(1008977.72))
//InitData: result convert string "1,008,977.72"

如果您想在每3位数字中设置分隔符,请从上面的代码中删除数字样式。 - Deepak Saki

13

你可以创建一个字符串或整数的扩展,我将展示一个字符串的例子

extension String{
     func toCurrencyFormat() -> String {
        if let intValue = Int(self){
           let numberFormatter = NumberFormatter()
           numberFormatter.locale = Locale(identifier: "ig_NG")/* Using Nigeria's Naira here or you can use Locale.current to get current locale, please change to your locale, link below to get all locale identifier.*/ 
           numberFormatter.numberStyle = NumberFormatter.Style.currency
           return numberFormatter.string(from: NSNumber(value: intValue)) ?? ""
      }
    return ""
  }
}

获取所有本地标识符的链接


2
你也可以使用 Locale.current 来获取当前设备的语言环境。 - devios1

12

最好的方法是创建NSNumberFormatter。(在Swift 3中称为NumberFormatter。)您可以请求货币,它将设置字符串以遵循用户的本地化设置,这非常有用。

如果不想使用NumberFormatter,而是想要强制使用美国格式的美元和美分字符串,则可以按照以下方式进行格式化:

let amount: Double = 123.45

let amountString = String(format: "$%.02f", amount)

11
除了其他人讨论的NumberFormatter或String(format:)之外,您可能还希望考虑使用Decimal或NSDecimalNumber,并自己控制舍入,从而避免浮点问题。如果您正在进行简单的小费计算器,则可能不需要这样做。但是,如果您要做像在一天结束时添加小费之类的事情,如果您不舍入数字和/或使用十进制数字进行数学运算,则可能会引入错误。
因此,请继续配置您的格式化程序:
let formatter: NumberFormatter = {
    let _formatter = NumberFormatter()
    _formatter.numberStyle = .decimal
    _formatter.minimumFractionDigits = 2
    _formatter.maximumFractionDigits = 2
    _formatter.generatesDecimalNumbers = true
    return _formatter
}()

接下来,使用十进制数:

let string = "2.03"
let tipRate = Decimal(sign: .plus, exponent: -3, significand: 125) // 12.5%
guard let billAmount = formatter.number(from: string) as? Decimal else { return }
let tip = (billAmount * tipRate).rounded(2)

guard let output = formatter.string(from: tip as NSDecimalNumber) else { return }
print("\(output)")

在哪里

extension Decimal {

    /// Round `Decimal` number to certain number of decimal places.
    ///
    /// - Parameters:
    ///   - scale: How many decimal places.
    ///   - roundingMode: How should number be rounded. Defaults to `.plain`.
    /// - Returns: The new rounded number.

    func rounded(_ scale: Int, roundingMode: RoundingMode = .plain) -> Decimal {
        var value = self
        var result: Decimal = 0
        NSDecimalRound(&result, &value, scale, roundingMode)
        return result
    }
}

显然,您可以将上述所有“2位小数”的引用替换为适合您使用的货币的数字(或可能使用变量来表示小数位数)。

1
为什么不在NumberFormatter中使用货币样式?并非所有货币都使用2位小数。 - rmaddy
2
是的,你可以这样做。而且你甚至可以查找应该舍入到几位小数。不可否认,使用.currency会引入其他问题(例如,字符串解析变得棘手;它假设你没有旅行并处理其他货币等)。根据应用程序的不同,有时让用户指定小数位数并完成它更容易。此外,我的重点不是格式化程序,而是一般建议避免在货币中使用浮点数运算。 - Rob
解决四舍五入问题的另一种方法是将“美元和美分”(或欧元、谢克尔或其他货币)乘以100(或乘以整个单位中小数单位的数量,对于没有100美分的货币)。然后使用整数运算,只需手动格式化输出以插入小数分隔符即可。这种方法也避免了浮点错误。 - Duncan C
嗯,Decimal 比简单的整数要复杂得多。它使用 BCD 表示十进制值,并且逐个十进制数字进行数学运算,而不是使用二进制数学和转换。它还支持分数值。因此,它比二进制浮点或整数数学慢得多。对于简单的计算,速度差异并不重要,但如果您正在进行大量计算,则差异可能相当显着。 - Duncan C
我同意小数可能会不够高效(虽然对于绝大多数应用程序来说并没有明显的影响),但在我看来,它是正确的抽象用于“如何使用货币进行数学运算”的问题。 - Rob

4
 extension String{
    func convertDoubleToCurrency() -> String{
        let amount1 = Double(self)
        let numberFormatter = NumberFormatter()
        numberFormatter.numberStyle = .currency
        numberFormatter.locale = Locale(identifier: "en_US")
        return numberFormatter.string(from: NSNumber(value: amount1!))!
    }
}

1
在 2022 年使用 Swift 5.5,我创建了扩展程序,可以将 Float 或 Double 转换为使用设备语言环境或您传递的语言环境的货币。您可以在这里查看:https://github.com/ahenqs/SwiftExtensions/blob/main/Currency.playground/Contents.swift
import UIKit

extension NSNumber {
    
    /// Converts an NSNumber into a formatted currency string, device's current Locale.
    var currency: String {
        return self.currency(for: Locale.current)
    }
    
    /// Converts an NSNumber into a formatted currency string, using Locale as a parameter.
    func currency(for locale: Locale) -> String {
        let numberFormatter = NumberFormatter()
        numberFormatter.usesGroupingSeparator = locale.groupingSeparator != nil
        numberFormatter.numberStyle = .currency
        numberFormatter.locale = locale
        
        return numberFormatter.string(from: self)!
    }
}

extension Double {
    
    /// Converts a Double into a formatted currency string, device's current Locale.
    var currency: String {
        return NSNumber(value: self).currency(for: Locale.current)
    }
    
    /// Converts a Double into a formatted currency string, using Locale as a parameter.
    func currency(for locale: Locale) -> String {
        return NSNumber(value: self).currency(for: locale)
    }
}

extension Float {
    
    /// Converts a Float into a formatted currency string, device's current Locale.
    var currency: String {
        return NSNumber(value: self).currency(for: Locale.current)
    }
    
    /// Converts a Float into a formatted currency string, using Locale as a parameter.
    func currency(for locale: Locale) -> String {
        return NSNumber(value: self).currency(for: locale)
    }
}

let amount = 3927.75 // Can be either Double or Float, since we have both extensions.
let usLocale = Locale(identifier: "en-US") // US
let brLocale = Locale(identifier: "pt-BR") // Brazil
let frLocale = Locale(identifier: "fr-FR") // France
print("\(Locale.current.identifier) -> " + amount.currency) // default current device's Locale.
print("\(usLocale.identifier) -> " + amount.currency(for: usLocale))
print("\(brLocale.identifier) -> " + amount.currency(for: brLocale))
print("\(frLocale.identifier) -> " + amount.currency(for: frLocale))

// will print something like this:
// en_US -> $3,927.75
// en-US -> $3,927.75
// pt-BR -> R$ 3.927,75
// fr-FR -> 3 927,75 €

我希望这能有所帮助,编程愉快!


1
这是我一直在使用的简单方法。
extension String {
    func toCurrency(Amount: NSNumber) -> String {
        var currencyFormatter = NumberFormatter()
        currencyFormatter.usesGroupingSeparator = true
        currencyFormatter.numberStyle = .currency
        currencyFormatter.locale = Locale.current

        return currencyFormatter.string(from: Amount)!
    }
}

被使用如下

let amountToCurrency = NSNumber(99.99)
String().toCurrency(Amount: amountToCurrency)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接