Convert NSSting to NSDecimalNumber based on locale - ios

I want to convert a number which is input as NSString to NSDecimalNumber based on NSLocale.
for example
InputString => 110.50
Current Locale => en_FI
Expected output => 110.50
Actual output => 110
InputString => 110.50
Current Locale => en_US
Expected output => 110.50
Actual output => 110.50
InputString => 110,50
Current Locale => en_US
Expected output => 110.50
Actual output => 110
InputString => 110.50
Current Locale => en_US
Expected output => 110.50
Actual output => 110.50
I am using the following code to achieve this but its not working.
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString:self.amountTextField.text locale:[NSLocale currentLocale]];
As mentioned above in 1st and 3rd i am not the desired output. Any idea what i am doing wrong here.

The only numbers that do not have the expected output have a comma instead of a period and a period instead of a comma in the input. Try replacing that and see if it works as some locals use different decimal separators.

I propose you to specify NumberFormatters decimalSeparator yourself and in input string replace , and . with same character you used in decimalSeparator.
import Foundation
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
func cleanNumberString(with string: String) -> String {
let knownDecimalSeparators = [",", "."]
let localSeparator = formatter.decimalSeparator ?? "."
var cleanString = string;
for separator in knownDecimalSeparators {
cleanString = cleanString.replacingOccurrences(of: separator, with: localSeparator)
}
return cleanString
}
func number(with string: String) -> Double {
let cleanString = cleanNumberString(with: string)
let number = formatter.number(from: cleanString)
return number?.doubleValue ?? 0
}
let string1 = "14,4"
let string2 = "14.4"
let cleanString1 = cleanNumberString(with: string1)
let cleanString2 = cleanNumberString(with: string2)
let number1 = number(with: string1)
let number2 = number(with: string2)
Same in Objective-C
#interface CleanNumberFormatter ()
#property (nonatomic) NSNumberFormatter *formatter;
#end
#implementation CleanNumberFormatter
- (instancetype)init
{
self = [super init];
if (self) {
self.formatter = [NSNumberFormatter new];
self.formatter.numberStyle = NSNumberFormatterDecimalStyle;
}
return self;
}
- (NSString *)cleanNumberStringWithString:(NSString *)string
{
NSArray *knownDecimalSeparators = #[#",", #"."];
NSString *localSeparator = self.formatter.decimalSeparator ?: #",";
NSString *cleanString = [string copy];
for (NSString *separator in knownDecimalSeparators) {
cleanString = [cleanString stringByReplacingOccurrencesOfString:separator withString:localSeparator];
}
return cleanString;
}
- (double)numberWithString:(NSString *)string
{
NSString *cleanString = [self cleanNumberStringWithString:string];
double number = [self.formatter numberFromString:cleanString].doubleValue;
return number;
}
- (void)foo
{
NSString *string1 = #"14.4";
NSString *string2 = #"14,4";
NSString *cleanString1 = [self cleanNumberStringWithString:string1];
NSString *cleanString2 = [self cleanNumberStringWithString:string2];
double number1 = [self numberWithString:string1];
double number2 = [self numberWithString:string2];
}
#end

Related

How to format a float value in Objective-C

- (NSString *)_stringToFloat:(NSNumber *)number {
if (number && number > 0) {
return [NSString stringWithFormat:#"%.2f",[number floatValue]];
}
return #"0.0";
}
Using:
_lblAppointmentFee.text = [[NSString alloc]initWithFormat:#"%#",[self_stringToFloat:[_dicObject objectForKeyNotNull:#"rate"]]];
How can I can return "5.21" for a value of 5.21 and return "5" for a value of 5.00?
Use an NSNumberFormatter. Set the desired fraction digits.
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.numberStyle = NSNumberFormatterDecimalStyle;
nf.minimumFractionDigits = 0;
nf.maximumFractionDigits = 2;
NSString *result = [nf stringFromNumber:#(5.12)];
This gives a result of "5.12" while:
result = [nf stringFromNumber:#(5.00)];
gives a result of "5".
This also has the added bonus of properly formatting the result based on the user's locale.

Transform punctuations form half width to full width

I have a sentence below:
我今天去买菜,买了一个西瓜,花了1.2元,买了一个土豆,花了3.78元。还买了一个无花果,花了45.89,怎么办呢?好贵呀!贵的我不知道再买什么了。
The punctuations in it are half width. How to change them to fullwidth, like the following:
我今天去买菜,买了一个西瓜,花了1.2元,买了一个土豆,花了3.78元。还买了一个无花果,花了45.89,怎么办呢?好贵呀!贵的我不知道再买什么了。
Some punctuations to consider (not exhaustive):
, to ,
? to ?
! to !
"" to “”
; to ;
First, define the CharacterSet from which you want to transform your characters. So if you want only punctuation, the set could be CharacterSet.punctuationCharacters or CharacterSet.alphanumerics.inverted.
Then map each character from this set to its HalfwidthFullwidth transformation.
Swift 3 and 4
extension String {
func transformingHalfwidthFullwidth(from aSet: CharacterSet) -> String {
return String(characters.map {
if String($0).rangeOfCharacter(from: aSet) != nil {
let string = NSMutableString(string: String($0))
CFStringTransform(string, nil, kCFStringTransformFullwidthHalfwidth, true)
return String(string).characters.first!
} else {
return $0
}
})
}
}
Usage
let string = ",?!\"\";abc012図書館 助け 足場が痛い 多くの涙"
let result = string.transformingHalfwidthFullwidth(from: CharacterSet.alphanumerics.inverted)
// it prints: ,?!"";abc012図書館 助け 足場が痛い 多くの涙
print(result)
Objective-C
#implementation NSString (HalfwidthFullwidth)
- (NSString *)transformingHalfwidthFullwidth:(nonnull NSCharacterSet *)aSet {
NSUInteger len = self.length;
unichar buffer[len + 1];
[self getCharacters:buffer range:NSMakeRange(0, len)];
for (int i = 0; i < len; i++) {
unichar c = buffer[i];
NSMutableString *s = [[NSMutableString alloc] initWithCharacters:&c length:1];
NSRange r = [s rangeOfCharacterFromSet:aSet];
if (r.location != NSNotFound) {
CFStringTransform((CFMutableStringRef)s, nil, kCFStringTransformFullwidthHalfwidth, true);
[s getCharacters:buffer + i range:NSMakeRange(0, 1)];
}
}
return [NSString stringWithCharacters:buffer length:len];
}
#end
Usage
NSString *string = #",?!\"\";abc012図書館 助け 足場が痛い 多くの涙";
NSString *result = [string transformingHalfwidthFullwidth:NSCharacterSet.alphanumericCharacterSet.invertedSet];
// it prints: ,?!"";abc012図書館 助け 足場が痛い 多くの涙
NSLog(result);
you can use CFStringTransform like :
Objective C :
NSString *string = #" ? \"\"!我今天去买菜,买了一个西瓜,花了1.2元,买了一个土豆,花了3.78元。还买了一个无花果,花了45.89,怎么办呢?好贵呀!贵的我不知道再买什么了";
NSMutableString *convertedString = [string mutableCopy];
CFStringTransform((CFMutableStringRef)convertedString, NULL, kCFStringTransformFullwidthHalfwidth, true);
NSLog(#"%#",convertedString);
Swift 3.0 :
let string = NSMutableString( string: " ? \"\"!我今天去买菜,买了一个西瓜,花了1.2元,买了一个土豆,花了3.78元。还买了一个无花果,花了45.89,怎么办呢?好贵呀!贵的我不知道再买什么了" )
CFStringTransform( string, nil, kCFStringTransformFullwidthHalfwidth, true )
print(string)

ios numberformatter currency remove trailing decimals if 0

I want to format my number into a currency string. These are the following cases
25.00 => $25
25.43 => $25.43
25.4 => $25.40
0.00 -> $0
Is there a way to do this in NSNumberFormatter?
This is my code right now:
NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
[fmt setNumberStyle:NSNumberFormatterCurrencyStyle];
[fmt setCurrencyCode:#"USD"];
However that fails for my first and last examples.
I also tried:
NSNumberFormatter *fmt = [[NSNumberFormatter alloc] init];
[fmt setPositiveFormat:#"$0.##"];
However that fails for my third case. Any suggestions?
Change the number of fraction digits based upon whether or not the number is whole.
- (NSString *)stringFromNumber:(NSNumber *)number
{
BOOL isWholeNumber = (roundf(number.doubleValue) == number.doubleValue);
self.currencyNumberFormatter.maximumFractionDigits = self.currencyNumberFormatter.minimumFractionDigits = isWholeNumber ? 0 : 2;
NSString *str = [self.currencyNumberFormatter stringFromNumber:number];
return str;
}
I don't think there's a way to do this using a plain NSNumberFormatter. You could set the minimum and maximum fraction digits to 0 just for formatting integers in a subclass of NSNumberFormatter:
#interface MyCurrencyFormatter : NSNumberFormatter
#end
#implementation MyCurrencyFormatter
- (id)init {
if ((self = [super init])) {
[self setNumberStyle:NSNumberFormatterCurrencyStyle]];
[self setCurrencyCode:#"USD"];
}
return self;
}
- (NSString *)stringFromNumber:(NSNumber *)aNumber {
NSInteger minimumFractionDigits = [self minimumFractionDigits];
NSInteger maximumFractionDigits = [self maximumFractionDigits];
if ([self isInteger:aNumber]) {
[self setMinimumFractionDigits:0];
[self setMaximumFractionDigits:0];
}
NSString *formattedNumber = [super stringFromNumber:aNumber];
[self setMinimumFractionDigits:minimumFractionDigits];
[self setMaximumFractionDigits:maximumFractionDigits];
return formattedNumber;
}
- (BOOL)isInteger:(NSNumber *)aNumber {
NSDecimal decimalValue = aNumber.decimalValue;
NSDecimalRound(&decimalValue, &decimalValue, 0, NSRoundDown);
NSDecimalNumber *roundedValue = [[NSDecimalNumber alloc] initWithDecimal:decimalValue]
return [aNumber isEqualToNumber:roundedValue];
}
#end
This should handle international number formats as well.
Credit to this post for determining if a number is an integer.
I am using the following solution in Swift. It is based on jowie's answer except I do not want to change maximumFractionDigits if my number is not whole. In some countries more than 2 digits are used for prices.
if(price==price.decimalNumberByRoundingAccordingToBehavior(nil))
{
numberFormatter.maximumFractionDigits=0
numberFormatter.minimumFractionDigits=0
}
let priceStr = numberFormatter.stringFromNumber(price)!
A "swifty" way to achieve the desired result, but remain flexible is to set a range by setting the minimum and maximum fraction digits to show:
let price: NSDecimalNumber // 299.0
let priceLocale: Locale // .current
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.locale = priceLocale
formatter.maximumFractionDigits = 2
formatter.minimumFractionDigits = 0
let result = formatter.string(from: price) ?? "" // 299 $

Format a UITextField for currency

I have a UITextField on my aplication that receives only numeric input from the user. This numeric input represents currency and have the default value of 0.00.
I would like to create something like a mask to format the UITextField as the user enter the numbers. For example:
9 becomes $0,09
99 becomes $0,99
999 becomes $999,99
The code below works great, but as I'm using integer and float values the app will eventually display wrong values afeter a certain point. For example:
999999999 becomes 100000000
That happens because flot and integer aren't precise enough and NSDEcimalNumber should be used. The point is that I can't figure out how to replace my integers and float values to NSDecimalNumber ones.
Does anyone could give me a hand to solve this? I spend some time searching the web for a solution but didn't find that suits my needs and tons of people with the same problem.
Heres the code:
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (textField.tag == 1){
NSString *cleanCentString = [[textField.text componentsSeparatedByCharactersInSet: [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:#""];
NSInteger centValue= cleanCentString.integerValue;
if (string.length > 0)
{
centValue = centValue * 10 + string.integerValue;
}
else
{
centValue = centValue / 10;
}
NSNumber *formatedValue;
formatedValue = [[NSNumber alloc] initWithFloat:(float)centValue / 100.0f];
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
textField.text = [_currencyFormatter stringFromNumber:formatedValue];
return NO;
}
if (textField.tag == 2){
// Nothing for now
}
return YES;
}
Implement UITextFieldDelegate and add next methods:
Swift:
let currencySign = "$"
// Adds $ before the text, e.g. "1" -> "$1" and allows "." and ","
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
var value = textField.text
var newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
var components = newString.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890,.").invertedSet)
var decimalString = "".join(components) as NSString
var length = decimalString.length
if length > 0 {
value = "\(currencySign)\(decimalString)"
}
else {
value = ""
}
textField.text = value
}
// Formats final value using current locale, e.g. "130,50" -> "$130", "120.70" -> "$120.70", "5" -> "$5.00"
func textFieldDidEndEditing(textField: UITextField) {
var value = textField.text
var components = value.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890,.").invertedSet)
var decimalString = "".join(components) as NSString
let number = NSDecimalNumber(string: decimalString, locale:NSLocale.currentLocale())
var formatter = NSNumberFormatter()
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
formatter.locale = NSLocale.currentLocale()
if let formatedValue = formatter.stringFromNumber(number) {
textField.text = formatedValue
}
else {
textField.text = ""
}
}
You can convert integer or double/float value to string by following:
NSString *str = [#(myInt) stringValue];
NSString *str1 = [[NSNumber numberWithFloat:myFloat] stringValue];
After that you can convert string to NSDecimalNumber by many ways. Like:
NSDecimalNumber *number = [NSDecimalNumber decimalNumberWithString:#"100.1"];
NSLog(#"%#", number);
NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithString:#"100.1" locale:NSLocale.currentLocale];
NSLog(#"%#",num);
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setLocale:[NSLocale currentLocale]];
NSLog(#"%#", [formatter stringFromNumber:number]);
NSNumberFormatter will help you with the right formatting. Please read apples page for NSDecimalNumber and NSNumberFormatter.
NSNumberFormatter contains a section named"Configuring the Format of Currency". I didn't try it, but it seems something that can help you.
Let me know if this helps.. :)

Convert String to Double with Currency

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
double value = [[numberFormatter numberFromString: ProviderTripRate.text] doubleValue];
[User setDouble:value forKey:#"Trip_Rate"];
NSLog(#" %# %f " , ProviderTripRate.text , value );
If the data inside ProviderTripRate.text is 2.75 then value is 0 wrong
If the data inside ProviderTripRate.text is $2.75 then value is 2.75 correct
How do you reliably convert from the string to a double value
NOTE that the text string may or may NOT have the local currency symbol ($ as shown above)
but it should always convert
Thanks in advance
Try this:
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle: NSNumberFormatterCurrencyStyle];
NSNumber* number = [numberFormatter numberFromString:ProviderTripRate.text]
if (!number) {
[numberFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
number = [numberFormatter numberFromString:ProviderTripRate.text];
}
double value = [number doubleValue];
Try this:
if ([providerTripRate.text hasPrefix:#"$"]) {
// use NSNumberFormatter to obtain the value
} else {
float theValue = [providerTripRate.text doubleValue];
}
etc.
extension String {
/// Creates a Double from the string
///
/// Tries various ways of extracting a number, currently handling the following formats
/// - 1000
/// - 1000.00
/// - 1,000.00
/// - $1,000.00
///
/// - Returns: Double value, or 0.0 if a non numerical String is passed
func asDouble() -> Double {
if let double = Double(self) {
return double
}
let formatter = NumberFormatter()
if let number = formatter.number(from: self) {
return number.doubleValue
}
formatter.numberStyle = .currency
if let number = formatter.number(from: self) {
return number.doubleValue
}
formatter.currencySymbol = ""
formatter.internationalCurrencySymbol = ""
if let number = formatter.number(from: self) {
return number.doubleValue
}
return 0
}
}

Resources