问题
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:
My solution:
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{
// Clear all characters that are not numbers
// (like currency symbols or dividers)
NSString *cleanCentString = [[textField.text
componentsSeparatedByCharactersInSet:
[[NSCharacterSet decimalDigitCharacterSet] invertedSet]]
componentsJoinedByString:@""];
// Parse final integer value
NSInteger centAmount = cleanCentString.integerValue;
// Check the user input
if (string.length > 0)
{
// Digit added
centAmount = centAmount * 10 + string.integerValue;
}
else
{
// Digit deleted
centAmount = centAmount / 10;
}
// Update call amount value
[_amount release];
_amount = [[NSNumber alloc] initWithFloat:(float)centAmount / 100.0f];
// Write amount with currency symbols to the textfield
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[_currencyFormatter setCurrencyCode:@"USD"];
[_currencyFormatter setNegativeFormat:@"-¤#,##0.00"];
textField.text = [_currencyFormatter stringFromNumber:_amount];
[_currencyFormatter release]
// Since we already wrote our changes to the textfield
// we don't want to change the textfield again
return NO;
}
回答2:
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.
- Create a UITextField, make it hidden, assign it a delegate, and then make it the first responder to summon the keyboard.
- 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.
- 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!
回答3:
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 :)
回答4:
Since I am lazy, and I can't stand having my input reformatted "to help me out" on the fly, I say:
Just let them enter a decimal number. When they leave the field, reformat it.
回答5:
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. :)
回答6:
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;
}
回答7:
I wrote an open source UITextField
subclass to handle this, available here:
https://github.com/TomSwift/TSCurrencyTextField
The approach I took is similar to what Lars Schneider suggests in his popular answer. But my version is fully encapsulated in a reusable component that you use anywhere, just like UITextField. If you choose to implement any UITextFieldDelegate methods you can, but this is not required.
来源:https://stackoverflow.com/questions/593230/struggling-with-currency-in-cocoa