Luchando con NSNumberFormatter en Swift por dinero
locale swift (8)
Estoy creando una aplicación de presupuesto que permite al usuario ingresar su presupuesto y las transacciones. Necesito permitir que el usuario ingrese tanto peniques como libras de campos de texto separados y deben formatearse junto con los símbolos de moneda. Esto funciona bien en este momento, pero me gustaría que esté localizado ya que actualmente solo funciona con GBP. He estado luchando para ocultar ejemplos de NSNumberFormatter del objetivo C a Swift.
Mi primer problema es el hecho de que necesito establecer los marcadores de posición para los campos de entrada para que sean específicos de la ubicación de los usuarios. P.ej. Libras y peniques, dólares y centavos, etc.
El segundo problema es que los valores ingresados en cada uno de los campos de texto, como 10216 y 32, deben formatearse y el símbolo de moneda específico de la ubicación de los usuarios debe agregarse. Entonces se convertiría en £ 10,216.32 o $ 10,216.32 etc ...
Además, necesito usar el resultado del número formateado en un cálculo. Entonces, ¿cómo puedo hacer esto sin tener problemas sin tener problemas con el símbolo de moneda?
Cualquier ayuda sería muy apreciada.
Detalles
xCode 9.2, Swift 4
Solución 1
import Foundation
extension String {
var toLocale: Locale {
return Locale(identifier: self)
}
}
extension Numeric {
func currency(numberStyle: NumberFormatter.Style = NumberFormatter.Style.currency, locale: String, groupingSeparator: String? = nil, decimalSeparator: String? = nil) -> String? {
return currency(numberStyle: numberStyle, locale: locale.toLocale, groupingSeparator: groupingSeparator, decimalSeparator: decimalSeparator)
}
func currency(numberStyle: NumberFormatter.Style = NumberFormatter.Style.currency, locale: Locale = Locale.current, groupingSeparator: String? = nil, decimalSeparator: String? = nil) -> String? {
if let num = self as? NSNumber {
let formater = NumberFormatter()
formater.locale = locale
formater.numberStyle = numberStyle
var formatedSting = formater.string(from: num)
if let separator = groupingSeparator, let localeValue = locale.groupingSeparator {
formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
}
if let separator = decimalSeparator, let localeValue = locale.decimalSeparator {
formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
}
return formatedSting
}
return nil
}
}
Uso
let price = 12423.42
print(price.currency() ?? "nil")
print(price.currency(numberStyle: .currencyISOCode) ?? "nil")
print(price.currency(locale: "es_ES") ?? "nil")
print(price.currency(locale: "es_ES", groupingSeparator: "_", decimalSeparator: ".") ?? "nil")
Resultado
Solución 2
import Foundation
extension String {
var toLocale: Locale {
return Locale(identifier: self)
}
}
class NumFormatter {
static var shared = NumFormatter()
public private(set) var formater = NumberFormatter()
public private(set) var groupingSeparator: String? = nil
public private(set) var decimalSeparator: String? = nil
public var locale: Locale {
return formater.locale
}
class func locale(string: String) -> NumFormatter.Type {
NumFormatter.shared.formater.locale = string.toLocale
return NumFormatter.self
}
class func number(style: NumberFormatter.Style = NumberFormatter.Style.currency) -> NumFormatter.Type {
NumFormatter.shared.formater.numberStyle = style
return NumFormatter.self
}
class func number(groupingSeparator: String?) -> NumFormatter.Type {
NumFormatter.shared.groupingSeparator = groupingSeparator
return NumFormatter.self
}
class func number(decimalSeparator: String?) -> NumFormatter.Type {
NumFormatter.shared.decimalSeparator = decimalSeparator
return NumFormatter.self
}
}
extension Numeric {
func currency() -> String? {
if let num = self as? NSNumber {
let formater = NumFormatter.shared.formater
var formatedSting = formater.string(from: num)
if let separator = NumFormatter.shared.groupingSeparator, let localeValue = formater.locale.groupingSeparator {
formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
}
if let separator = NumFormatter.shared.decimalSeparator, let localeValue = formater.locale.decimalSeparator {
formatedSting = formatedSting?.replacingOccurrences(of: localeValue, with: separator)
}
return formatedSting
}
return nil
}
}
Uso
let price = 12423.42
print(price.currency() ?? "nil")
NumFormatter.number(style: .currencyISOCode)
print(price.currency() ?? "nil")
NumFormatter.locale(string: "es_ES")
print(price.currency() ?? "nil")
NumFormatter.number(groupingSeparator: "_").number(decimalSeparator: ".")
print(price.currency() ?? "nil")
Aquí hay un ejemplo sobre cómo usarlo en Swift 3. ( Editar : también funciona en 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 €"
Aquí está el viejo ejemplo sobre cómo usarlo en 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 €"
Swift 4
formatter.locale = Locale.current
si quieres cambiar la configuración regional, puedes hacerlo de esta manera
formatter.locale = Locale.init(identifier: "id-ID")
// Esta es la configuración regional de Indonesia. si desea usar según el área del teléfono móvil, utilícelo según la mención superior Locale.corrent
//MARK:- Complete code
let formatter = NumberFormatter()
formatter.numberStyle = .currency
if let formattedTipAmount = formatter.string(from: Int(newString)! as
NSNumber) {
yourtextfield.text = formattedTipAmount
}
También implementé la solución proporcionada por @ NiñoScript como una extensión:
Extensión
// 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)!
}
}
Uso:
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)!
}
}
agregar esta función
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
}
utilizando:
let giaTri = value as! Int
myGuessTotalCorrect = addSeparateMarkForNumber(int: giaTri)
Swift 3:
Si está buscando una solución que le brinde:
- "5" = "$ 5"
- "5.0" = "$ 5"
- "5,00" = "$ 5"
- "5.5" = "$ 5.50"
- "5,50" = "$ 5,50"
- "5,55" = "$ 5,55"
- "5.234234" = "5.23"
Por favor use lo siguiente:
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)"
}
Xcode 9 • Swift 4
extension Locale {
static let br = Locale(identifier: "pt_BR")
static let us = Locale(identifier: "en_US")
static let uk = Locale(identifier: "en_UK")
}
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 { // for Swift 3 use FloatingPoint or Int
var currency: String {
return Formatter.currency.string(for: self) ?? ""
}
var currencyUS: String {
return Formatter.currencyUS.string(for: self) ?? ""
}
var currencyBR: String {
return 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"
extension Float {
var convertAsLocaleCurrency :String {
var formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = Locale.current
return formatter.string(from: self as NSNumber)!
}
}
Esto funciona para swift 3.1 xcode 8.2.1