Боротьба з NSNumberFormatter у Swift за валюту


86

Я створюю бюджетну програму, яка дозволяє користувачеві вводити свій бюджет, а також транзакції. Мені потрібно дозволити користувачеві вводити як пенси, так і фунти з окремих текстових полів, і їх потрібно форматувати разом із символами валют. На даний момент у мене все працює нормально, але я хотів би зробити його локалізованим, оскільки наразі він працює лише з GBP. Я намагався приховати приклади NSNumberFormatter від Objective C до Swift.

Моє перше питання полягає в тому, що мені потрібно встановити заповнювачі для полів введення як специфічні для розташування користувачів. Напр. Фунти та пенси, долари та центи тощо ...

Друге питання полягає в тому, що значення, введені в кожне з текстових полів, таких як 10216 та 32, повинні бути відформатовані та доданий символ валюти, який відповідає місцезнаходженню користувачів. Тож це становило б 10 216,32 фунтів стерлінгів або 10 216,32 доларів США тощо ...

Крім того, мені потрібно використовувати результат відформатованого числа в розрахунку. То як я можу це зробити, не натрапивши на проблеми, не натрапивши на проблеми із символом валюти?

Будь-яка допомога буде дуже вдячна.


2
Ви можете розмістити приклад неробочого коду?
NiñoScript

Відповіді:


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

На основі прикладу, який ви навели, мені вдалося впровадити форматування чисел у свою програму, так що цей біт сортується. Тепер мені просто потрібно зрозуміти, як встановити заповнювачі текстового поля на основі розташування користувачів.
користувач3746428

2
Не потрібно передавати його в NSNumber, ви можете використовувати метод форматування func рядок (для obj: Будь-який?) -> Рядок ?. Тож вам просто потрібно використовувати string(for: price)замістьstring(from: price)
Лео Дабус

1
@LeoDabus ти маєш рацію, я не знав про цей метод, але я не впевнений, чи варто мені редагувати свою відповідь, оскільки я вважаю, що я скоріше використовую API NumberFormatter і явно буду використовувати NSNumber, а не дозволяю це неявно кинути всередину.
NiñoScript

Зверніть увагу, що результат formatter.string (від :) є необов’язковим рядком, а не рядком (як це випливає з коментарів), тому перед використанням потрібно буде розгортати.
Алі Бідл

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:)
Лео Дабус

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

розширення повинно розширювати FloatingPoint для версії та рядка Swift 3 (з: метод - для NSNumber. Для типів FlotingPoint потрібно використовувати рядок (для :). Я опублікував розширення Swift 3
Лео Дабус

Не використовуйте типи валют для валюти, використовуйте десяткові.
аднако

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

Оновлено для Swift 4 з відповіді @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 ?? ""
    }
}

Примітка: відсутність розгортання сили, розгортання сили - це зло.


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

Це працює для швидкого 3.1 xcode 8.2.1


Хоча цей фрагмент коду вітається і може надати певну допомогу, його було б значно покращено, якби він містив пояснення того, як і чому це вирішує проблему. Пам’ятайте, що ви відповідаєте на запитання читачам у майбутньому, а не лише тому, хто задає зараз! Будь ласка, відредагуйте свою відповідь, щоб додати пояснення, та вкажіть, які обмеження та припущення застосовуються.
Тобі Спейт,

Не використовуйте типи валют для валюти, використовуйте десяткові.
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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.