Punctuation in UIText field causing wrong behaviour when checking intValue - ios

If an entered value is under 200 in a UIText field, an alert is generated.
If the user adds > in the field to signify its greater than 200, the alert is still fired. Obviously I dont want this so I need some way of ignoring this punctuation or even better all punctuation
Defaulting to numeric values only is not an option
if ([_myTextBox.text intValue]< 200){
//alert is fired
}

Your problem is that whenever a string doesn't start from a valid decimal number, intValue returns 0. The correct way is to use number formatters:
NSNumberFormatter *formatter = [NSNumberFormatter new];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *value = [formatter numberFromString:_myTextBox.text];
// if _myTextBox.text is not a valid number, value will be nil
if (number && ([number intValue] < 200) {
// Place your code here
}

Related

Format NSDecimalNumber to currency by country code without loss of precision

So right now I have the following code:
- (NSString*)convertToLocalCurrencyFormat:(NSDecimalNumber*)result {
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
formatter.currencyCode = self.comparisonCurrency;
formatter.usesSignificantDigits = YES;
return [formatter stringFromNumber:result];
}
When I pass in an NSDecimalNumber* containing 678071967196719797153475347466.94627863, it gets formatted to ¥678,072,000,000,000,000,000,000,000,000 (with the currencyCode set to JPY). If I leave out the formatter.usesSignificantDigits = YES line, then it gets formatted to ¥678,071,967,196,719,797,153,475,347,467, closer, but still dropping the decimal and following values.
However, when I pass in 6780.0416000000012517376, it's formatted correctly to ¥6,780.04 with the significant digits line. It gets formatted to ¥6,780 without the significant digits line.
I know that NSNumberFormatter can take in any NSNumber as a parameter, but can only deal with values as precise as doubles, leaving NSDecimalNumber with no errors and incorrect results.
How can I format NSDecimalNumbers with currency codes without loss of precision?
Thanks
Try setting the minimum fraction digits instead:
formatter.minimumFractionDigits = 2;
HTH

How to determine if locale currency has a decimal point

Is there anyway in objective-c to determine if a currency uses a decimal point (regardless of declared NSNumber type)?
I have multiple locales and I use NSNumberFormatter (based on locale) to set string currency string style, however before-hand I would like to know if the selected locale currency uses a decimal point.
[_cf setLocale:
[NSLocale localeWithLocaleIndentifier:[NSString stringWithFormat:#"%#",locale]]]];
[cf setNumberStyle:NSNUmberFormatterCurrencyStyle];
NSString *value = [cf stringFromNumber:price];
return value;
After creating the NSNumberFormatter with currency style, ask the formatter how many fraction digits it has.
NSInteger maxFractionDigits = cf.maximumFractionDigits;
if (maxFractionDigits == 0) {
// this currency is an integer, not a decimal
}
One example where this is true is the Japanese Yen (¥).

Using NSNumberFormatter to generate number with trailing zeros after decimal

In Swift, how would you create an NSNumberFormatter that would preserve trailing zeros after a decimal (12.000) while also generating a number appropriate for the current locale?
My current code and example output:
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.locale = NSLocale.currentLocale()
formatter.maximumFractionDigits = 10
var doubleNumString = "12.0"
println(formatter.numberFromString(doubleNumString)) //in English it prints 12, want it to print 12.0
var doubleNumString = "12.000"
println(formatter.numberFromString(doubleNumString)) //prints 12, want it to print 12.000
var doubleNumString = "12.125"
println(formatter.numberFromString(doubleNumString)) //prints 12.125 as expected
var doubleNumString = "1234"
println(formatter.numberFromString(doubleNumString)) //prints 1,234 as expected
I've already coded it such that if the string ends in a decimal ("12.") then it won't use this formatter to generate the number and will instead just display the number then the decimal (but I will need to improve that because some languages read right to left).
One solution would be to check if the string contains a period and if so, check if all digits that follow it are 0, and if so then don't run it through the number formatter and instead run only the int value through the formatter then append/prepend the decimal followed by the appropriate number of 0's.
Is there a better/cleaner solution?
As mentioned by Martin R, you can set the minimumFractionDigits and maximumFractionDigits to the same number which will enforce that many fraction digits always be displayed. To know how many to display you need to take a substring after the decimal to the end and count its elements. To know whether or not all of the fraction digits are 0's, I created a helper method that converts that substring to a number and if it equals 0 then you know they were all 0's.
Unfortunately you need to convert the string to a localized number using a couple different NSNumberFormatters based on the original string number. So if it does contain a decimal and everything after it is a 0 then you need to create a different formatter, convert the string to a number, then convert that number to a string in order to display it respecting the user's locale. Otherwise you can just use your original number formatter.
This function takes care of your requirement. pass same for & from locale (e.g. en_US)
+ (NSString*) stringForString:(NSString*) string forLocale:(NSString*) toLocaleCode fromLocal:(NSString*) fromLocaleCode {
NSLocale *fromLocale = [[NSLocale alloc] initWithLocaleIdentifier:fromLocaleCode];
NSNumberFormatter *sourceFormatter = [[NSNumberFormatter alloc] init];
[sourceFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[sourceFormatter setUsesGroupingSeparator:NO];
[sourceFormatter setLocale:fromLocale];
NSNumber *localizedNumber = [sourceFormatter numberFromString:string];
if (!localizedNumber) {
return string;
}
NSLocale *toLocale = [[NSLocale alloc] initWithLocaleIdentifier:toLocaleCode];
NSNumberFormatter *destinationFormatter = [[NSNumberFormatter alloc] init];
[destinationFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[destinationFormatter setUsesGroupingSeparator:NO];
[destinationFormatter setLocale:toLocale];
NSString *localizedString = [destinationFormatter stringFromNumber:localizedNumber];
//add the zeros which were dropped because of the sourceDecimalString number conversion e.g. 0.20 is converted to 0.2
if (localizedString.length < string.length) {
NSRange rangeOfDecimal = [string rangeOfString:sourceFormatter.decimalSeparator];
if (rangeOfDecimal.location != NSNotFound) {
NSString* sourceDecimalString = [string substringFromIndex:rangeOfDecimal.location];
rangeOfDecimal = [localizedString rangeOfString:destinationFormatter.decimalSeparator];
if (rangeOfDecimal.location != NSNotFound) {
NSString* destinationDecimalString = [localizedString substringFromIndex:rangeOfDecimal.location];
if (destinationDecimalString.length < sourceDecimalString.length) {
int difference = sourceDecimalString.length - destinationDecimalString.length;
int toalDecimalDigits = (destinationDecimalString.length - 1) + difference; //-1 to remove '.'
destinationFormatter.minimumFractionDigits = toalDecimalDigits;
destinationFormatter.maximumFractionDigits = toalDecimalDigits;
localizedString = [destinationFormatter stringFromNumber:localizedNumber];
}
}
else{//this indicates no decimal separator in the return string
int toalDecimalDigits = (sourceDecimalString.length - 1); //-1 to remove '.'
destinationFormatter.minimumFractionDigits = toalDecimalDigits;
destinationFormatter.maximumFractionDigits = toalDecimalDigits;
localizedString = [destinationFormatter stringFromNumber:localizedNumber];
}
}
}
return localizedString;
}

iOS Currency input field

I would like to have a currency input field in my app where the user can either include the currency symbol (as appropriate for their locale), or not, as they please.
I have a text field set up and I am storing the value in a NSDecimalNumber (which I understand is the recommended way to store currency).
The following code will get me from an NSDecimalNumber to a formatted currency string:
[NSNumberFormatter localizedStringFromNumber:currencyValue numberStyle:NSNumberFormatterCurrencyStyle]
But I can't find a way to do the reverse of that. i.e., take the string that the user has typed into my text field and convert it (if possible) into an NSDecimalNumber. Keeping in mind that the currency symbol may be there (because it came from the function above) or not (because the user didn't bother to type the currency symbol).
What am I missing?
If I can't figure this out I will just not accept any currency symbol at all (i.e., just parse it using the code below). But it seems better to allow the currency symbol.
[NSDecimalNumber decimalNumberWithString:currencyString locale:[NSLocale currentLocale]]
I have the feeling I am missing something. What's the right way to convert back and forth between a localized currency string and an NSDecimalNumber?
If you get an instance of an NSNumberFormatter (instead of using the static method call) you can use the NSNumberFormatter's "stringFromNumber" method to format the currency string and use "numberFromString" to parse that string back into a number.
https://developer.apple.com/library/mac/documentation/cocoa/reference/foundation/Classes/NSNumberFormatter_Class/Reference/Reference.html
If you are sure that the currency symbol, if it exists, will be at the beginning of the string, then you can just use this (string is the NSString that the user enters and number is the NSDecimalNumber that represents the value of the currency)
NSDecimalNumber *number;
if([string hasPrefix:#"0"] || [string hasPrefix:#"1"] || [string hasPrefix:#"2"] || [string hasPrefix:#"3"] || [string hasPrefix:#"4"] || [string hasPrefix:#"5"] || [string hasPrefix:#"6"] || [string hasPrefix:#"7"] || [string hasPrefix:#"8"] || [string hasPrefix:#"9"]) {
// The string does not contain a currency symbol in the beginning so we can just assign that to a NSDecimalNumber
number = [NSDecimalNumber decimalNumberWithString:string];
}
else {
// The string contains a currency symbol at the beginning and we will assign the currency symbol to the currencySymbol variable
NSString *currencySymbol;
currencySymbol = [string substringWithRange:NSMakeRange(0,1)];
number = [NSDecimalNumber decimalNumberWithString:[string stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:#""];
}
This should do the trick:
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setLenient:YES];
NSString* input = #"$3,456.78";
NSNumber *number = [currencyFormatter numberFromString:input];
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]];

Check to see if UITextField has a numeric value greater than 0

I have a UITextField and I only want a number greater than 0 ( I don't want non-numeric characters or the value 0 )
This is how I check to see if it is empty:
if(seizure.text.length==0)
This is how I check to see if it is equal to 0:
else if(seizure.text doubleValue]==0)
How can I check for non-numeric characters?
First check to see if you have any characters in the string, then check to make sure that it only contains numeric characters, and finally check to see if the value is greater than 0:
if (seizure.text.length > 0)
{
NSCharacterSet *nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if ([seizure.text rangeOfCharacterFromSet:nonNumbers].location == NSNotFound)
{
if ([seizure.text doubleValue] > 0)
{
// Text Field contains a numeric value greater than 0
NSLog(#"Good number.");
return;
}
}
}
// If we make it to here, it does not meet your requirements.
NSLog(#"Bad Number.");
NSScanner will do the job nicely here. Unlike -[NSString doubleValue], its scanDouble: can parse and then also tell you whether it consumed the entire string, so you will know that there are non-numerical characters present.
Demonstration on some test cases. See the comments for descriptions of the expected results.
NSArray * texts = #[// First four unacceptable because non-numeric
#"", #"Hello, world!", #"1.0 excelsior", #"Jiminy 1.0 Crickets",
// These three unacceptable because 0 or less
#"0.0" #"0", #"-2048",
// Last three are good
#"3.14159", #"1", #"10000000000.0"];
for( NSString * text in texts ){
NSScanner * scanner = [NSScanner scannerWithString:text];
double val;
[scanner scanDouble:&val];
// Scanned the whole string and ended up with a positive value
if( [scanner isAtEnd] && val > 0 ){
NSLog(#"'%#'? I accept.", text);
}
else {
NSLog(#"'%#' is no good.", text);
}
}
[seizure.text doubleValue] == 0 will be true either if seizure.text is a textual representation of zero or if it doesn't contain a valid textual representation of a number (see the documentation for doubleValue).
In other words if this expression is false then you have a string value which starts with a number. However you still don't know if you string value contains only a number, e.g. [#"2.5 miles" doubleValue] has the value 2.5. If you need to handle strings like this you should look at NSScanner.

Resources