Swift 5: Format Float Number for zero negative number - ios

I want to format a negative number and be left with only two decimal places.
But when the number is a negative -0.00 I want it to be unsigned. For example:
let negativeNumber = -0.008
let result = 0.00
I try do this with numberFormatter but i get this result -0.00, and i want this result 0.00
This is the formatter i use:
static func formatValue(_ value: Double, currency: Currency? = nil, options: [FormatterOption] = [], locale: Locale = Locale.current) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = locale
let defaultOptions: [FormatterOption] = [.minFractionDigits(2),
.maxFractionDigits(currency?.decimals ?? 2),
.roundingMode(.down)]
var completeDecimals = false
for option in defaultOptions + options {
switch option {
case .minFractionDigits(let minValue):
if let decimals = currency?.decimals, minValue > decimals {
continue
}
formatter.minimumFractionDigits = minValue
case .maxFractionDigits(let maxValue):
if let decimals = currency?.decimals, maxValue > decimals {
continue
}
formatter.maximumFractionDigits = maxValue
case .roundingMode(let mode):
formatter.roundingMode =
}
let formattedNumber = formatter.format(from: value)
return formattedNumber
}

Related

Swift automatically add "." on every 3 digit input in textfield [duplicate]

I am fairly new to Swift and having a great deal of trouble finding a way to add a space as a thousand separator.
What I am hoping to achieve is taking the result of a calculation and displaying it in a textfield so that the format is:
2 358 000
instead of
2358000
for example.
I am not sure if I should be formatting the Int value and then converting it to a String, or adding the space after the Int value is converted to a String. Any help would be greatly appreciated.
You can use NSNumberFormatter to specify a different grouping separator as follow:
update: Xcode 11.5 • Swift 5.2
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = " "
return formatter
}()
}
extension Numeric {
var formattedWithSeparator: String { Formatter.withSeparator.string(for: self) ?? "" }
}
2358000.formattedWithSeparator // "2 358 000"
2358000.99.formattedWithSeparator // "2 358 000.99"
let int = 2358000
let intFormatted = int.formattedWithSeparator // "2 358 000"
let decimal: Decimal = 2358000
let decimalFormatted = decimal.formattedWithSeparator // "2 358 000"
let decimalWithFractionalDigits: Decimal = 2358000.99
let decimalWithFractionalDigitsFormatted = decimalWithFractionalDigits.formattedWithSeparator // "2 358 000.99"
If you need to display your value as currency with current locale or with a fixed locale:
extension Formatter {
static let number = NumberFormatter()
}
extension Locale {
static let englishUS: Locale = .init(identifier: "en_US")
static let frenchFR: Locale = .init(identifier: "fr_FR")
static let portugueseBR: Locale = .init(identifier: "pt_BR")
// ... and so on
}
extension Numeric {
func formatted(with groupingSeparator: String? = nil, style: NumberFormatter.Style, locale: Locale = .current) -> String {
Formatter.number.locale = locale
Formatter.number.numberStyle = style
if let groupingSeparator = groupingSeparator {
Formatter.number.groupingSeparator = groupingSeparator
}
return Formatter.number.string(for: self) ?? ""
}
// Localized
var currency: String { formatted(style: .currency) }
// Fixed locales
var currencyUS: String { formatted(style: .currency, locale: .englishUS) }
var currencyFR: String { formatted(style: .currency, locale: .frenchFR) }
var currencyBR: String { formatted(style: .currency, locale: .portugueseBR) }
// ... and so on
var calculator: String { formatted(groupingSeparator: " ", style: .decimal) }
}
Usage:
1234.99.currency // "$1,234.99"
1234.99.currencyUS // "$1,234.99"
1234.99.currencyFR // "1 234,99 €"
1234.99.currencyBR // "R$ 1.234,99"
1234.99.calculator // "1 234.99"
Note: If you would like to have a space with the same width of a period you can use "\u{2008}"
unicode spaces
formatter.groupingSeparator = "\u{2008}"
You want to use NSNumberFormatter:
let fmt = NSNumberFormatter()
fmt.numberStyle = .DecimalStyle
fmt.stringFromNumber(2358000) // with my locale, "2,358,000"
fmt.locale = NSLocale(localeIdentifier: "fr_FR")
fmt.stringFromNumber(2358000) // "2 358 000"
With Swift 5, when you need to format the display of numbers, NumberFormatter is the right tool.
NumberFormatter has a property called numberStyle. numberStyle can be set to a value of NumberFormatter.Style.decimal in order to set the formatter's style to decimal.
Therefore, in the simplest case when you want to format a number with decimal style, you can use the following Playground code:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = NumberFormatter.Style.decimal
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
According to the user's current locale, this code will print Optional("2,358,000") for en_US or Optional("2 358 000") for fr_FR.
Note that the following code snippet that uses the NumberFormatter's locale property set to Locale.current is equivalent to the previous Playground code:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = Locale.current
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
The Playground code below that uses the NumberFormatter's groupingSeparator property set to Locale.current.groupingSeparator is also equivalent to the former:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = Locale.current.groupingSeparator
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
Otherwise, if you want to set the number formatting with a specific locale formatting style, you may use the following Playground code:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = Locale(identifier: "fr_FR")
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
// prints: Optional("2 358 000")
However, if what you really want is to enforce a specific grouping separator, you may use the Playground code below:
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.groupingSeparator = " "
let amount = 2358000
let formattedString = formatter.string(for: amount)
print(String(describing: formattedString))
// prints: Optional("2 358 000")
Leo Dabus's answer translated to Swift 3:
Into any .swift file, out of a class:
struct Number {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.groupingSeparator = " " // or possibly "." / ","
formatter.numberStyle = .decimal
return formatter
}()
}
extension Integer {
var stringWithSepator: String {
return Number.withSeparator.string(from: NSNumber(value: hashValue)) ?? ""
}
}
Usage:
let myInteger = 2358000
let myString = myInteger.stringWithSeparator // "2 358 000"
Code:
//5000000
let formatter = NumberFormatter()
formatter.groupingSeparator = " "
formatter.locale = Locale(identifier: "en_US")
formatter.numberStyle = .decimal.
Output:
5 000 000
I was looking for a currency format like $100,000.00
I accomplished it customizing the implementation Leo Dabus like this
extension Formatter {
static let withSeparator: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyGroupingSeparator = ","
formatter.locale = Locale(identifier: "en_US") //for USA's currency patter
return formatter
}()
}
extension Numeric {
var formattedWithSeparator: String {
return Formatter.withSeparator.string(for: self) ?? ""
}
}
Try this
func addPoints(inputNumber: NSMutableString){
var count: Int = inputNumber.length
while count >= 4 {
count = count - 3
inputNumber.insert(" ", at: count) // you also can use ","
}
print(inputNumber)
}
The call:
addPoints(inputNumber: "123456")
The result:
123 456 (or 123,456)

NumberFormatter for decimal place in Swift

I have the following code which works fine except keeping 0 after decimal. The goal is to format the user input as user types on Textfield
MyClass {
static let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.locale = NSLocale.current
formatter.maximumFractionDigits = 2
return formatter
}()
}
extension Double {
func formatted() -> String{
return MyClass.amountFormatter.string(from: NSNumber(value: self)) ?? "0"
}
}
This gives me the below output
1256.formatted - 1,256
1256.00.formatted - 1,256 (Need to retain 0 after decimal, desired result - 1,256.00)
1256.05.formatted - 1,256.05
1256.80.formatted - 1,256.8 (Need to retain 0 after decimal, desired result 1,256.80)
1256.5789847.formatted - 1,256.57
How to achieve the desired output

How to convert Decimal to String with two digits after separator?

This is what I do now:
extension Decimal {
var formattedAmount: String {
let formatter = NumberFormatter()
formatter.generatesDecimalNumbers = true
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter.string(from: self) //mismatch type
}
}
but I cannot create NSNumber from Decimal.
This should work
extension Decimal {
var formattedAmount: String? {
let formatter = NumberFormatter()
formatter.generatesDecimalNumbers = true
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
return formatter.string(from: self as NSDecimalNumber)
}
}
This:
formatter.string(from: ...) // requires a NSNumber
You can either do this:
formatter.string(for: self) // which takes Any as argument
Or:
string(from: self as NSDecimalNumber) // cast self to NSDecimalNumber

Only show decimal portion of Double with the decimal point

I'm trying to only show the decimal portion of a number but it keeps printing the number with the decimal point:
for number 223.50:
changeAmountLabel.text = "\(balance.truncatingRemainder(dividingBy: 1))"
this will print .5, but what needs to be printed is 50, is there a way to do this?
You can do this by using a NumberFormatter.
Try this:
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 3
let number = 223.50
if let str = formatter.string(from: NSNumber(value: number)) as? String {
if let range = str.range(of: ".") {
let value = str.substring(from: range.upperBound)
print(value) // 50
}
}
Update for Swift 4:
if let str = formatter.string(for: number) {
if let range = str.range(of: ".") {
let value = str[range.upperBound...]
print(value) // 50
}
}
You just need to floor your balance and subtract the result from it, next multiply it by 100. After that you can format the result as needed:
let balance = 223.50
let cents = (balance - floor(balance)) * 100
let formatted = String(format: "%.0f", cents) // "50"

Changing grouping separator, currency symbol and position of currency symbol for multiple currencies in Swift

My app uses multiple currencies, and these currencies uses different formats, for example:
Price for Ruble shows as: 1,101 Руб.
Same amount for US Dollar shows as: US $1 101
How would I change the grouping separator, currency symbol and position of currency symbol, by defining a set of different formats for different currencies.
This is how my short code stands
var formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
formatter.locale = NSLocale.currentLocale()
formatter.stringFromNumber(4500000)
//Output : $4,500,000.00
//Expected : 4,500,000 Руб.
Swift 4 or later
extension Formatter {
static let belarusianRuble: NumberFormatter = {
let formatter = NumberFormatter()
// set the numberStyle to .CurrencyStyle
formatter.numberStyle = .currency
// set the desired negative and positive formats grouping, and currency symbol position
formatter.positiveFormat = "#,##0 ¤"
formatter.negativeFormat = "-#,##0 ¤"
// set your custom currency symbol
formatter.currencySymbol = "Руб"
return formatter
}()
}
let stringToDisplay = Formatter.belarusianRuble.string(for: 4500000) // "4,500,000 Руб"
extension Formatter {
static let currencyBYR: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.positiveFormat = "#,##0 ¤"
formatter.negativeFormat = "-#,##0 ¤"
formatter.currencySymbol = "Руб"
return formatter
}()
static let currencyEUR: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "pt_PT")
formatter.numberStyle = .currency
return formatter
}()
static let currencyUSD: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_US")
formatter.numberStyle = .currency
return formatter
}()
static let currencyBRL: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "pt_BR")
formatter.numberStyle = .currency
return formatter
}()
static let currencyRUB: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "ru_RU")
formatter.numberStyle = .currency
formatter.maximumFractionDigits = 0
return formatter
}()
static let currencyLocale: NumberFormatter = {
let formatter = NumberFormatter()
formatter.locale = .current
formatter.numberStyle = .currency
return formatter
}()
}
extension Numeric {
var currencyLocale: String { return Formatter.currencyLocale.string(for: self) ?? "" }
var currencyUSD: String { return Formatter.currencyUSD.string(for: self) ?? "" }
var currencyEUR: String { return Formatter.currencyEUR.string(for: self) ?? "" }
var currencyBYR: String { return Formatter.currencyBYR.string(for: self) ?? "" }
var currencyBRL: String { return Formatter.currencyBRL.string(for: self) ?? "" }
var currencyRUB: String { return Formatter.currencyRUB.string(for: self) ?? "" }
}
Usage
let amount = 4500000.0
let stringLocale = amount.currencyLocale // "$4,500,000.00"
let stringUSD = amount.currencyUSD // "$4,500,000.00"
let stringEUR = amount.currencyEUR // "4 500 000,00 €"
let stringBRL = amount.currencyBRL // "R$ 4.500.000,00"
let stringBYR = amount.currencyBYR // "4,500,000 Руб"
let stringRUB = amount.currencyRUB // "4 500 000 ₽"
I ended up with Currency class that uses current locale decimal/thousands/grouping separators (thanks to #jcaron recommendation in comments)
The class offers few customization, which fits my task:
Currency symbol
Minimum/maximum fraction digits
Positioning symbol either left/right
class Currency {
var formatter = NumberFormatter()
var symbol: String
var isRTL: Bool
init(_ currencySymbol: String, minFractionDigits: Int, maxFractionDigits: Int, isRTL: Bool) {
self.formatter.currencySymbol = ""
self.formatter.minimumFractionDigits = minFractionDigits
self.formatter.maximumFractionDigits = maxFractionDigits
self.formatter.numberStyle = .currency
self.symbol = currencySymbol
self.isRTL = isRTL
}
func beautify(_ price: Double) -> String {
let str = self.formatter.string(from: NSNumber(value: price))!
if self.isRTL {
return str + self.symbol
}
return self.symbol + str
}
}
Initialize required formatters
struct CurrencyFormatter {
static let byr = Currency(" Руб.", minFractionDigits: 2, maxFractionDigits: 2, isRTL: true)
static let usd = Currency("US $", minFractionDigits: 2, maxFractionDigits: 2, isRTL: false)
static let rub = Currency("\u{20BD} ", minFractionDigits: 0, maxFractionDigits: 1, isRTL: false)
}
Usage
CurrencyFormatter.byr.beautify(12345.67) // Output: 12 345,67 Руб.
CurrencyFormatter.usd.beautify(12345.67) // Output: US $12 345,67
CurrencyFormatter.rub.beautify(12345.67) // Output: ₽ 12 345,7

Resources