Numbers arent formatting with Decimal style - ios

So i have an app which should format the value string in a SecondviewController to display on the firstViewController tab.
When a user enters numbers, its suppose to format it using a comma every 3 digits. But somehow i cant seem to get how to display it onto the firstViewController. Here is my code:
In secondViewController.m
-(IBAction) button:(id)sender{
NSString *string1 = self.addDigitField.text;
/* format the entered digits */
NSNumberFormatter *format = [NSNumberFormatter new];
[format setNumberStyle: NSNumberFormatterDecimalStyle];
NSNumber *n = [format numberFromString: addDigitField];
/* display the string1 value to the firstViewController Label*/
self.string1 = [self. addDigitField text]
FirstViewcontroller *first = self.tabBarController.viewControllers[0];
first.fvcString = self.string1;
}
now firstViewController.m
/* display the value into firstViewController label *
-(void) viewDidAppear:(BOOL) animated {
If (self.svcString != nil)
self.displayValue.text = self.svcString;
}
So my problem is, where in this code do i put the the message method to format the
Number ? The code i am trying to add is as follow :
[format stringFromNumber: n];
I typically use this way when doing it all on a single view app:
self.label.text = [format stringFromNumber: n];
And it formats it. But in my case of formatting the string to a firstViewcontroller
From the secondViewcontroller is where i am lost, any suggestion where i am going
Wrong ??

In your second view controller, replace
first.svcString = self.string1;
with
first.svcString = [format stringFromNumber:n];

The problem is in this line below :-
self.string1 = [self. addDigitField text];
In this your are setting the text value which is the previous value. You need to set the NSNumber value which you have converted using number formatter. Try like this
NSNumber *n = [format numberFromString:
addDigitField];
self.string1 = [format stringFromNumber:n];
FirstViewcontroller *first =
self.tabBarController.viewControllers[0];
first.fvcString = self.string1;
Now in your first view controller just replace the below line
self.displayValue.text = self.fvcString;

Related

Global NSDate variable seems to make app crash

I have a global variable in FirstViewController.h
extern NSString *dateString;
it records the date/time of when I press saveText button. There is another button called readText which pushes a UITableView. The first cell on this table will print the date saved.
However, this only works if I first press the saveText button. Otherwise, if I just press readText, it crashes. How can I get it so that if there is no current datetime saved, that it doesn't crash?
Here is where I wrote the timestamp for saveButton:
-(IBAction)saveText:(id)sender{
//code to save text, irrelevant to the question//
dateString = [NSDateFormatter localizedStringFromDate:[NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
NSLog(#"%#",dateString);
}
and here is the code to load the tableviewcells with the timestamp
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *str = #"Text- saved on: ";
str = [str stringByAppendingString: dateString];
self.list = [[NSArray alloc] initWithObjects: str, nil];
}
In my methods that use the variable dateString,
I used:
if([dateString length] == 0){
//perform standard procedure
}
else {
//what I want it to do
}

Float value resets after Adding

I start off with a number, lets say 250. I add all sorts of numbers, but anytime I add a high number like 2,000 it adds correctly. Then I add 3. The new number comes out to 5 like it thought 2,000 was 2.0. I do not know why it is doing this.
float start = self.amountLabel.text.floatValue;
float changeAmount = self.amountField.text.floatValue;
float newValue;
if (determConfirm == 1) {
newValue = start + changeAmount;
} else {
newValue = start - changeAmount;
}
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:5];
[formatter setRoundingMode: NSNumberFormatterRoundUp];
NSString *numberString = [formatter stringFromNumber:[NSNumber numberWithFloat:newValue]];
[[NSUserDefaults standardUserDefaults] setValue:numberString forKey:#"newValue"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.amountLabel.text = numberString;
self.amountField.text = #"0.00";
[self.amountField resignFirstResponder];
The problem is the use of floatValue to convert the formatted number text to a number. floatValue only works as expected if the text is unformatted (no commas) and uses the period for the decimal separator.
Since you store a formatted number in the field, it only works with small numbers and in certain locales.
Replace your use of floatValue on the text with the same NSNumberFormatter used to format the number. Use it to parse the text and give you an NSNumber (which you can then call floatValue on).
Just a guess as it's hard by looking at your code, but maybe you want this:
float newValue = start;
if (determConfirm == 1) {
newValue += changeAmount;
} else {
newValue -= changeAmount;
}

Having trouble using NSNumberFormatter for currency conversion in iOS

I have a UITextField that receives numeric input from the user in my application. The values from this textfield then get converted into currency format using NSNumberFormatter within my shouldChangeCharactersInRange delegate method. When I enter the number "12345678", the number gets correctly converted to $123456.78 (the numbers are entered one digit at a time, and up to this point, everything works smoothly). However, when I enter another digit after this (e.g. 9), rather than displaying "1234567.89", the number "1234567.88" is displayed. If I enter another number after that, a totally different numbers after this (I'm using the number key pad in the application to enter the numbers. Here is the code that I have:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];
textField.text = modifiedValue;
The line that causes this unusual conversion is this one:
modifiedValue = [formatter stringFromNumber:[NSNumber numberWithFloat:[modifiedValue floatValue]]];
Can anyone see why this is?
It's likely to be a rounding error when doing the string->float conversion. You shouldn't use floats when dealing with currency. You could use a NSDecimalNumber instead.
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
// Below 2 lines if converting from a "currency" string
NSNumber *modifiedNumber = [formatter numberFromString:modifiedValue]; // To convert from the currency string to a number object
NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithDecimal:[modifiedNumber decimalValue]];
// OR the below line if converting from a non-currency string
NSDecimalNumber *decimal = [NSDecimalNumber decimalNumberWithString:modifiedValue];
modifiedValue = [formatter stringFromNumber:decimal]; // Convert the new decimal back to a currency string
You may also consider making the number formatter lenient - often helps with user entered data.
[formatter setLenient:YES];
When I'm running number conversions to currency, I usually run this code:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *text = _textField.text;
NSString *decimalSeperator = #".";
NSCharacterSet *charSet = nil;
NSString *numberChars = #"0123456789";
// the number formatter will only be instantiated once ...
static NSNumberFormatter *numberFormatter;
if (!numberFormatter)
{
[numberFormatter setLocale:[NSLocale currentLocale]];
numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.numberStyle = NSNumberFormatterCurrencyStyle;
numberFormatter.maximumFractionDigits = 10;
numberFormatter.minimumFractionDigits = 0;
numberFormatter.decimalSeparator = decimalSeperator;
numberFormatter.usesGroupingSeparator = NO;
}
// create a character set of valid chars (numbers and optionally a decimal sign) ...
NSRange decimalRange = [text rangeOfString:decimalSeperator];
BOOL isDecimalNumber = (decimalRange.location != NSNotFound);
if (isDecimalNumber)
{
charSet = [NSCharacterSet characterSetWithCharactersInString:numberChars];
}
else
{
numberChars = [numberChars stringByAppendingString:decimalSeperator];
charSet = [NSCharacterSet characterSetWithCharactersInString:numberChars];
}
// remove amy characters from the string that are not a number or decimal sign ...
NSCharacterSet *invertedCharSet = [charSet invertedSet];
NSString *trimmedString = [string stringByTrimmingCharactersInSet:invertedCharSet];
text = [text stringByReplacingCharactersInRange:range withString:trimmedString];
// whenever a decimalSeperator is entered, we'll just update the textField.
// whenever other chars are entered, we'll calculate the new number and update the textField accordingly.
if ([string isEqualToString:decimalSeperator] == YES)
{
textField.text = text;
}
else
{
NSNumber *number = [numberFormatter numberFromString:text];
if (number == nil)
{
number = [NSNumber numberWithInt:0];
}
textField.text = isDecimalNumber ? text : [numberFormatter stringFromNumber:number];
}
return NO; // we return NO because we have manually edited the textField contents.
}
The link explaining this is Re-Apply currency formatting to a UITextField on a change event
Hope this works!

How do I set a currency string value to a UITextField, preserving encoding?

I am developing an application in which I wish to handle different currency formats, depending on the current locale. Using a NSNumberFormatter I can correctly translate a number into string and back without problems.
But, if I put the string value into a UITextField and later get it back, I won't be able to convert the string back into a number and I will get a nil value instead.
Here is a sample code to explain the problem:
NSNumberFormatter *nf = [Utils currencyFormatter];
NSNumber *n = [NSNumber numberWithInt:10000];
NSString *s = [nf stringFromNumber:n];
NSLog(#"String value = %#", s);
UITextField *t = [[UITextField alloc] init];
// I put the string into the text field ...
t.text = s;
// ... and later I get the value back
s = t.text;
NSLog(#"Text field text = %#", s);
n = [nf numberFromString:s];
NSLog(#"Number value = %d", [n intValue]);
where the currencyFormatter method is defined this way:
+ (NSNumberFormatter *)currencyFormatter
{
static NSNumberFormatter *currencyFormatter;
if (!currencyFormatter) {
currencyFormatter = [[NSNumberFormatter alloc] init];
[currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[currencyFormatter setLocale:[NSLocale currentLocale]];
if ([currencyFormatter generatesDecimalNumbers] || [[currencyFormatter roundingIncrement] floatValue] < 1) {
[currencyFormatter setGeneratesDecimalNumbers:YES];
[currencyFormatter setRoundingIncrement:[NSNumber numberWithFloat:0.01]];
}
}
return currencyFormatter;
}
(The inner if is used to force the formatter to always round to the smallest decimal digit, eg. even for CHF values).
What I get in the Console is this:
2012-03-29 00:35:38.490 myMutuo2[45396:fb03] String value = € 10.000,00
2012-03-29 00:35:38.494 myMutuo2[45396:fb03] Text field text = € 10.000,00
2012-03-29 00:35:38.497 myMutuo2[45396:fb03] Number value = 0
The strange part is that the spacing character between € and 1 in the first line is represented in the console through a mid-air dot, while in the second line this dot disappears. I believe this is an encoding-related problem.
Can anyone help me solve this problem?
Thank you!
Edit
I changed my test code to this:
NSNumberFormatter *nf = [Utils currencyFormatter];
NSNumber *n = [NSNumber numberWithInt:10000];
NSString *s = [nf stringFromNumber:n];
NSLog(#"String value = %# (space code is %d)", s, [s characterAtIndex:1]);
UITextField *t = [[UITextField alloc] init];
t.text = s;
s = t.text;
NSLog(#"Text field text = %# (space code is %d)", s, [s characterAtIndex:1]);
n = [nf numberFromString:s];
NSLog(#"Number value = %d", [n intValue]);
to discover this:
2012-03-29 02:29:43.402 myMutuo2[45993:fb03] String value = € 10.000,00 (space code is 160)
2012-03-29 02:29:43.405 myMutuo2[45993:fb03] Text field text = € 10.000,00 (space code is 32)
2012-03-29 02:29:43.409 myMutuo2[45993:fb03] Number value = 0
The NSNumberFormatter writes down the space as a non-breaking space (ASCII char 160), and then the UITextField re-encodes that space as a simple space (ASCII char 32). Any known workaround for this behaviour? Perhaps I could just make a replacement of the space with a non-breaking space but ... will it work for all the locales?
A possible workaround: You could try to parse only the number values (and punctiation) via a regex pattern and create you currency value based on that number. If you do it in that way it is perhaps even more forgivable for the user, if he typed another currency symbol or other symbols that shouldnt be there...
I was able to solve this problem only extending the UITextField through a custom class. In this new class I put a new #property of type NSString in which I store the "computed" string value for the text field. This string will never be modified and will preserve the original encoding of the content of the text field.
When you need to work again on the original untouched content of the text field you have to refer to this new property instead of referring to the text property.
Using a separate string container is the only way to avoid these strange encoding changes.

How to insert grouping comma in NSString as typed?

A user enters a numerical string in a UILabel and the text is displayed as the user types.
NSString *input = [[sender titleLabel] text];
[display_ setText:[[display_ text] stringByAppendingString:input]];
This works fine and I format the display using NSNumberFormatter so that if 1000000 is entered it is converted to 1,000,000 upon tapping another button.
However, I'd like to get those grouping commas to be displayed as the user types. I can understand how to insert things into strings, but how to do it as the user types is not clear to me. Would this require a mutable string?
Maybe somehow monitor the string length and split it into groups of three and make and display a new string with the commas inserted? I could probably do that, but it is the "as it is typed" part that has me stymied.
Another thought is to append and display the string, then read the display into a new NSString and format it and display it again right away. So I tried that, and it almost works:
if (userIsEntering)
{
NSNumberFormatter *fmtr = [[NSNumberFormatter alloc] init];
[fmtr setNumberStyle:NSNumberFormatterDecimalStyle];
[fmtr setGroupingSeparator:#","];
[fmtr setDecimalSeparator:#"."];
NSString *out = [[display_ text] stringByAppendingString:digit];
NSNumber *num = [fmtr numberFromString:out];
NSString* formattedResult = [fmtr stringFromNumber:num];
[display_ setText: [NSString stringWithFormat:#"%#", formattedResult]];
[fmtr release];
}
And, along with the fact that the formatter is created and released with every digit entered, after 4 digits it returns null.
UPDATE: I figured out how to do it in a label (with some help from #Michael-Frederick). It uses an NSNotification.
This works perfectly for non-decimal numbers, but when I try to enter a decimal point it is ignored and removed. If I do not invoke this method, the decimal point is accepted and all works well.
Numeric entry is as follows (from a button):
NSString *digit = [[sender titleLabel] text];
if (userIsStillWorking_)
{
[display_ setText:[[display_ text] stringByAppendingString:digit]];
}
else
{
[display_ setText: digit];
userIsStillWorking_ = YES;
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateDisplay" object:nil];
And the updateDisplay method called by the notification is:
{
NSString *unformattedValue = [display_.text stringByReplacingOccurrencesOfString:
#"," withString:#""];
unformattedValue = [unformattedValue stringByReplacingOccurrencesOfString:
#"." withString:#""];
NSDecimalNumber *amount = [NSDecimalNumber decimalNumberWithString:unformattedValue];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setGroupingSeparator:#","];
[formatter setDecimalSeparator:#"."];
[display_ setText: [ formatter stringFromNumber:amount]];
[formatter release];
}
I've tried commenting out
unformattedValue = [unformattedValue stringByReplacingOccurrencesOfString:
#"." withString:#""];
but that makes no difference.
EDIT:
A user cannot type into a uilabel. You need to use either a uitextfield or a uitextview.
If you want to use a uitextfield, do something like this...
- (void) viewDidLoad {
[super viewDidLoad];
[textField addTarget:self action:#selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
}
- (void) textFieldDidChange:(UITextField *)textField {
NSString *unformattedValue = [textField.text stringByReplacingOccurrencesOfString:#"," withString:#""];
unformattedValue = [unformattedValue stringByReplacingOccurrencesOfString:#"." withString:#""];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setGroupingSeparator:#","];
[formatter setDecimalSeparator:#"."];
NSNumber *amount = [NSNumber numberWithInteger:[unformattedValue intValue]];
textField.text = [formatter stringFromNumber:amount];
[formatter release];
}
Note that you are correct that NSNumberFormatter should be declared outside of the textFieldDidChange method. Note that this code would actually be for an integer. You could have to switch intValue to floatValue if need be. This code is untested, it is more of a general guide.
The best way to do this is to use 2 UIlabels. 1 of the labels is used to feed your NSNumberFormatter object by using [NSString stringByAppendingString:digit]; The other label is actually displayed. The trick is to set the label that is unformatted to hidden and the other label is set as an output for the number formatter. By feeding the hidden label to the number formatter, and outputting the displayed label from the number formatter, the number formatter should be set to the NSDecimalNumber style. Setting it all up this way, the result displayed is automatic commas while typing.

Resources