views:

2156

answers:

6

I'm trying to do something I'd think would be fairly simple: Let a user input a dollar amount, store that amount in an NSNumber (NSDecimalNumber?), then display that amount formatted as currency again at some later time.

My trouble is not so much with the setNumberStyle:NSNumberFormatterCurrencyStyle and displaying floats as currency. The trouble is more with how said numberFormatter works with this UITextField. I can find few examples. This thread from November and this one give me some ideas but leaves me with more questions.

I am using the UIKeyboardTypeNumberPad keyboard and understand that I should probably show $0.00 (or whatever local currency format is) in the field upon display then as a user enters numerals to shift the decimal place along:

  • Begin with display $0.00
  • Tap 2 key: display $0.02
  • Tap 5 key: display $0.25
  • Tap 4 key: display $2.54
  • Tap 3 key: display $25.43

Then [numberFormatter numberFromString:textField.text] should give me a value I can store in my NSNumber variable.

Sadly I'm still struggling: Is this really the best/easiest way? If so then maybe someone can help me with the implementation? I feel UITextField may need a delegate responding to every keypress but not sure what, where and how to implement it?! Any sample code? I'd greatly appreciate it! I've searched high and low...

Edit1: So I'm looking into NSFormatter's stringForObjectValue: and the closest thing I can find to what benzado recommends: UITextViewTextDidChangeNotification. Having really tough time finding sample code on either of them...so let me know if you know where to look?

+1  A: 

Here's the rough plan of attack I'd use if I had to write that now. The trick will be typing into a hidden UITextField and updating a UILabel with the formatted value as the user types.

  1. Create a UITextField, make it hidden, assign it a delegate, and then make it the first responder to summon the keyboard.
  2. In your delegate, respond to the textDidChange: message (too lazy to look up the exact name) by taking the text field's new value and converting it to a number. Make sure empty string converts to zero.
  3. Run the number through your formatter, and update a UILabel with that formatted currency value.

On every key press, the label will be updated, so the user will feel as though she is editing the formatted value, when she is really editing the hidden text field. How sneaky!

benzado
This is really frustrating as it seems like a lot of hoops just to accept a currency amount? Isn't there a simpler way? Shouldn't the NSNumberFormatter be able to handle accepting digits and displaying them as currency... and the opposite of converting displayed currency to an NSNumber for storage?
Meltemi
That procedure isn't that hard. The Cocoa equivalent is 10–20 lines.
Peter Hosey
UITextField does not have support for formatters like NSTextField (on Mac OS X) does. Sorry if that means your job is not easy.
benzado
If it's not obvious, I'm still puzzled. All I'm looking for is a link to some sample code (do you remember learning Cocoa?). But thanks for making me feel like even more of an idiot and for the down vote because it's so clear to newcomers that this is the ONLY forum w/out chronological threading.
Meltemi
For example, the closest thing I can find to step #2 in your response is UITextFieldTextDidChangeNotification in UITextField Class Reference. But I find little/no documentation on that.
Meltemi
You might find it helpful to read the desktop Cocoa documentation, since a lot of it applies to Cocoa Touch. Read about delegates, specifically. Have some patience, there won't always be sample code to cut and paste for everything you want to do.
benzado
A: 

Is there any sample code for this? I was hoping that some use of an an NSNumberFormatter with style NSNumberFormatterCurrencyStyle would be enough. But I'm clearly not getting this.

Apologies for my ineptitude but if anyone could point to some sample code out there for "simply" entering, displaying currency on the iPhone I would greatly appreciate it (and hopefully shut up and quit asking questions about this).

Thank you!

Meltemi
Downvoting because you are not using the website correctly. Edit your question for this sort of thing, don't post another answer-which-is-not-an-answer.
benzado
Hmm.. i can't even figure out how to use the forum... great!
Meltemi
Its important to note that Stack Overflow is not a forum - its a QA site. Comments and updating the question are the two things you use to communicate as the asker. When you moved this into the question please delete this answer.
Georg Fritzsche
Yeah this might have helped back in Feb '09 when I made my first post...but 16 months later and 1,181 in Rep, I think I've figured it out...
Meltemi
A: 

Well, this is a bit late but I figured I would give this a shot anyway. This is probably more of a workaround and may be messy code, but it worked for me. I connected a label and a hidden textfield in IB, and I wrote this code using the UITextFieldDelegate delegate.

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    if (textField == fineHiddenTextField) {
     NSString *fineText = [textField.text stringByReplacingCharactersInRange:range withString:string];
     if ([fineText length] == 0) {
      NSString *emptyFine = @"0.00";
      float fineValue = [emptyFine floatValue];
      fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
     } else if ([fineText length] == 1) {
      NSString *firstDec = [NSString stringWithFormat:@"0.0%@", fineText];
      float fineValue = [firstDec floatValue];
      fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
     } else if ([fineText length] == 2) {
      NSString *secondDec = [NSString stringWithFormat:@"0.%@", fineText];
      float fineValue = [secondDec floatValue];
      fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
     } else {
      int decimalPlace = [fineText length] - 2;
      NSString *fineDollarAmt = [fineText substringToIndex:decimalPlace];
      NSString *fineCentsAmt = [fineText substringFromIndex:decimalPlace];
      NSString *finalFineValue = [NSString stringWithFormat:@"%@.%@", fineDollarAmt, fineCentsAmt];
      float fineValue = [finalFineValue floatValue];
      fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
     }


     //fineEntryLabel.text = [NSString stringWithFormat:@"%.2f", fineValue];
     return YES;
    }
}

Not exactly the neatest, but it really got the job done. The initial if statement was just to make sure that this was only happening for this one particular textField (As there are multiple throughout the same page.) This code was intended for the input of money (The amount of a fine paid) and I wanted it to be simple and easy to use. Just set your label to align from the right, and it should.

I am a little wired on coffee right now, but I will answer any questions you may have. :)

JonLim
A: 

Here's my solution!

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
 float valorUnit = [textField.text floatValue];

 if( string.length > 0)
 {
  float incremento = [string floatValue];

  valorUnit = valorUnit * 10.f + (incremento/100.f);
  NSString* strVal = [NSString stringWithFormat:@"%f", valorUnit];

  if (valorUnit > 0.f && valorUnit < 10.f) {
   textField.text  = [strVal substringToIndex:3];
  }
  else if (valorUnit < 100.f && valorUnit >= 10.f)
  {
   textField.text  = [strVal substringToIndex:4];
  }
  else if (valorUnit >=100.f && valorUnit <1000.f)
  {
   textField.text  = [strVal substringToIndex:5];
  }
  else {
   return NO;
  }

 }
 else {
  valorUnit = (valorUnit/10.f);
  NSString* strVal = [NSString stringWithFormat:@"%f", valorUnit];
  if (valorUnit > 0.f && valorUnit < 10.f) {
   textField.text  = [strVal substringToIndex:5];
  }
  else if (valorUnit < 100.f && valorUnit >= 10.f)
  {
   textField.text  = [strVal substringToIndex:6];
  }
  else if (valorUnit >=100.f && valorUnit <1000.f)
  {
   textField.text  = [strVal substringToIndex:7];
  }
  else {
   return NO;
  }
 }

return YES;

}

pedro
You should use `localizedStringWithFormat:` to show the correct decimal separator. Does this solution handle non-fractional currencies (e.g., the Japanese yen) correctly? Does it handle currencies without a ¹⁄₁₀₀th unit correctly?
Peter Hosey
A: 

Well, i think this is better:

- (void)viewWillDisappear:(BOOL)animated{ [[NSNotificationCenter defaultCenter] removeObserver:self  name:UITextFieldTextDidChangeNotification object:nil]; }

-(void)viewDidAppear:(BOOL)animated{
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChanged:) name:UITextFieldTextDidChangeNotification object:nil];
}

-(void)textDidChanged:(NSNotification *)notification{
 [[NSNotificationCenter defaultCenter] removeObserver:self  name:UITextFieldTextDidChangeNotification object:nil];
 UITextField * textField= [notification object];
 NSString * sinPesos= [textField.text stringByReplacingOccurrencesOfString:@"$" withString:@""];
 NSString * sinPuntos= [sinPesos stringByReplacingOccurrencesOfString:@"." withString:@""];

 float monto = [sinPuntos floatValue]; 

 monto= monto/100;

 NSString * cardText= [[self montoFormatter] stringFromNumber:[NSNumber numberWithDouble:monto]]; 

 textField.text = ([cardText isEqualToString: @"0"] ? @"":cardText);
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChanged:) name:UITextFieldTextDidChangeNotification object:nil];
} 

-(NSNumberFormatter *)montoFormatter{
 NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
 [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
 [numberFormatter setMaximumFractionDigits:2];
 return [numberFormatter autorelease];
}

Try it out :)

Dario Lencina
+1  A: 
Lars Schneider