问题
I have a UITextField
in my application that is supposed to receive only numeric input from the user. This numeric input is supposed to represent currency (i.e. have only two decimal places), have the default value of 0.00 when nothing has been entered. I also would like to have a "$" sign that is on the far left, with the number right aligned, such that the number moves from right to left like such:
e.g. when entering the number "1234.56".
1. $ 0.00.
2. $ 0.01.
3. $ 0.12.
4. $ 1.23.
5. $ 12.34.
6. $ 123.45.
7. $ 1,234.56.
8. and so on...
I have the UITextField
right aligned so that it already moves the numbers over to the left from the right. However, I need to place the "$" inside the UITextField, and I would like the default 0.00 value to be modified to the number entered by the user one digit at a time. The user should NOT have to enter the decimal point, as it should already be in the textfield holding its position. I then would like the number to have a "," to be automatically entered to separate every three digits. I would like the application to do this AS THE USER IS ENTERING THE NUMBERS, and not formatted afterwards when everything is entered. How would I do this? I have seen a lot of people say that I am to use NSNumberFormatter
, but I want to know how do I use it in conjunction with the UITextField
such that it formats the numeric text on the fly, as described above?
I have found the following question posted on StackOverflow some time ago:
What is the best way to enter numeric values with decimal points?
but I want to know how to actually incorporate it as a solution to my problem.
回答1:
Here's a nice way to do it. Remember to set your UITextField to self in the viewDidLoad method and your header file must conform to the UITextFieldDelegate protocol
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
NSString *cleanCentString = [[textField.text componentsSeparatedByCharactersInSet: [[NSCharacterSet decimalDigitCharacterSet] invertedSet]] componentsJoinedByString:@""];
NSInteger centValue = [cleanCentString intValue];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
NSNumber *myNumber = [f numberFromString:cleanCentString];
NSNumber *result;
if([textField.text length] < 16){
if (string.length > 0)
{
centValue = centValue * 10 + [string intValue];
double intermediate = [myNumber doubleValue] * 10 + [[f numberFromString:string] doubleValue];
result = [[NSNumber alloc] initWithDouble:intermediate];
}
else
{
centValue = centValue / 10;
double intermediate = [myNumber doubleValue]/10;
result = [[NSNumber alloc] initWithDouble:intermediate];
}
myNumber = result;
NSLog(@"%ld ++++ %@", (long)centValue, myNumber);
NSNumber *formatedValue;
formatedValue = [[NSNumber alloc] initWithDouble:[myNumber doubleValue]/ 100.0f];
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
textField.text = [_currencyFormatter stringFromNumber:formatedValue];
return NO;
}else{
NSNumberFormatter *_currencyFormatter = [[NSNumberFormatter alloc] init];
[_currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
textField.text = [_currencyFormatter stringFromNumber:00];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle: @"Deposit Amount Limit"
message: @"You've exceeded the deposit amount limit. Kindly re-input amount"
delegate: self
cancelButtonTitle:@"Cancel"
otherButtonTitles:@"OK",nil];
[alert show];
return NO;
}
return YES;
}
回答2:
Updated Answer for SWIFT 3
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
if string.characters.count > 0 {
amountTypedString += string
let decNumber = NSDecimalNumber(string: amountTypedString).multiplying(by: 0.01)
let newString = "$" + formatter.string(from: decNumber)!
textField.text = newString
} else {
amountTypedString = String(amountTypedString.characters.dropLast())
if amountTypedString.characters.count > 0 {
let decNumber = NSDecimalNumber(string: amountTypedString).multiplying(by: 0.01)
let newString = "$" + formatter.string(from: decNumber)!
textField.text = newString
} else {
textField.text = "$0.00"
}
}
return false
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
amountTypedString = ""
return true
}
回答3:
Here is my solution in Swift. My approach was keeping track of the numbers entered and multiplying it by 0.01 and setting the result number with 2 decimal points using NSNumberFormatter. Note that I have the textfield keyboard set as the number pad.
amountTextfield?.keyboardType = UIKeyboardType.NumberPad
var amountTypedString = ""
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let formatter = NSNumberFormatter()
formatter.minimumFractionDigits = 2
formatter.maximumFractionDigits = 2
if string.characters.count > 0 {
amountTypedString += string
let decNumber = NSDecimalNumber(float: Float(amountTypedString)!).decimalNumberByMultiplyingBy(0.01)
let newString = "$" + formatter.stringFromNumber(decNumber)!
textfield.text = newString
} else {
amountTypedString = String(amountTypedString.characters.dropLast())
if amountTypedString.characters.count > 0 {
let decNumber = NSDecimalNumber(float: Float(amountTypedString)!).decimalNumberByMultiplyingBy(0.01)
let newString = "$" + formatter.stringFromNumber(decNumber)!
textfield.text = newString
} else {
textfield.text = "$0.00"
}
}
return false
}
func textFieldShouldClear(textField: UITextField) -> Bool {
amountTypedString = ""
return true
}
回答4:
You likely need to check out the UITextFieldDelegate
protocol. By assigning something (most commonly your ViewController class) as the UITextField's delegate, you'll be able to receive interactive text entry events from the control. In particular:
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
By returning NO from that method, you can prevent the text field from applying the keys pressed by the user.
However, even though you return NO there's nothing to prevent you from using the information passed to the delegate method to do your own manipulation of the text field contents in code.
In pseudo-code form:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
if (string-contains-number) {
alter textfield.text to be what it should be now, adding the number;
}
else {
string contains something you don't wan the user typing, so ignore it.
}
// Tell iOS not to apply the user's change (since you just did by
// updating the text yourself:
return NO;
}
Disclaimer: I haven't written actual code to test, so it's possible that altering the code inside this delegate method causes another shouldChangeCharactersInRange: message to fire. I don't believe that's the case, though. I don't believe the delegate event fires when you alter the text in code.
This obviously isn't as good as a custom control that already implements all the rules for a number text entry box (I'm having flashbacks to years of Visual Basic programming now...). But I don't know of any such control that someone's written and open-sourced.
回答5:
I have taken the answer from julien and modified it so other locales with differing currency formats are supported. I have also stored the number formatters for reuse so they are not created each time which is an expensive operation.
Overflow is handled by ignoring any input that would cause the stored value to overflow.
This solution assumes that text was entered only at the end of the text field, and as such will only operate on the end regardless of where the text cursor was prior to input.
let outputFormatter: NSNumberFormatter = {
let formatter = NSNumberFormatter()
formatter.numberStyle = .CurrencyStyle
if formatter.maximumFractionDigits > 0 {
formatter.multiplier = pow(10, -CGFloat(formatter.maximumFractionDigits))
}
return formatter
}()
let inputFormatter: NSNumberFormatter = {
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
return formatter
}()
private var value: UInt = 0
var currencyValue: NSDecimalNumber {
let exponent = Int16(truncatingBitPattern: outputFormatter.maximumFractionDigits)
return NSDecimalNumber(mantissa: UInt64(value), exponent: -exponent, isNegative: false)
}
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
if string.isEmpty {
/* User pressed backspace, shift right one space. */
value /= 10
} else if let number = inputFormatter.numberFromString(string)?.unsignedIntegerValue {
/* User entered a number, shift left one space and add. */
let shiftedValue = UInt.multiplyWithOverflow(value, 10)
let addedValue = UInt.addWithOverflow(shiftedValue.0, number)
if !shiftedValue.overflow && !addedValue.overflow {
value = addedValue.0
}
}
textField.text = outputFormatter.stringFromNumber(NSDecimalNumber(unsignedInteger: value))
return false
}
func textFieldShouldClear(textField: UITextField) -> Bool {
value = 0
return true
}
Using this in a playground with various locales gave me these results (sorry, can't embed directly yet).
来源:https://stackoverflow.com/questions/15217124/trying-to-format-a-uitextfield-to-behave-like-a-currency-calculator-for-only-num