尝试将 UITextField 格式化为像货币计算器一样仅用于 iOS 中的数字输入

Posted

技术标签:

【中文标题】尝试将 UITextField 格式化为像货币计算器一样仅用于 iOS 中的数字输入【英文标题】:Trying to format a UITextField to behave like a currency calculator for only numeric input in iOS 【发布时间】:2013-03-05 06:20:58 【问题描述】:

我的应用程序中有一个UITextField,它应该只接收来自用户的数字输入。这个数字输入应该代表货币(即只有两位小数),当没有输入任何内容时,默认值为 0.00。我还想在最左边有一个 "$" 符号,数字右对齐,这样数字就可以像这样从右向左移动:

例如输入数字时“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.等等……

我将UITextField 右对齐,以便它已经将数字从右侧移到左侧。但是,我需要将 "$" 放在 UITextField 中,并且我希望将默认的 0.00 值修改为输入的数字由用户一次一位数。用户不必输入小数点,因为它应该已经在保持其位置的文本字段中。然后,我希望该号码有一个 "," 自动输入以分隔每三个数字。我希望应用程序执行此操作因为用户正在输入数字,并且在输入所有内容后不进行格式化。我该怎么做?我看到很多人说我要使用NSNumberFormatter,但我想知道如何将它与UITextField 结合使用,以便如上所述即时格式化数字文本?

我前段时间在 *** 上发现了以下问题:

What is the best way to enter numeric values with decimal points?

但我想知道如何将它作为我的问题的解决方案。

【问题讨论】:

对于CurrencyField Swift 方法,请查看post 【参考方案1】:

这是一个很好的方法。记得在 viewDidLoad 方法中设置你的 UITextField 为 self 并且你的头文件必须符合 UITextFieldDelegate 协议

-(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;

【讨论】:

我认为这应该被标记为正确答案。它就像一个魅力。谢谢! @RZO92 谢谢!很高兴我能帮忙 这种方法错误地假设编辑总是在字符串的末尾进行,这忽略了用户将光标放在其他地方的可能性(这可以通过客户 UITextField 来缓解)。此外,如果执行财务计算,您应该真正使用 NSDecimalNumber。 为避免在美分以下四舍五入,请添加此行_currencyFormatter.roundingMode = NSNumberFormatterRoundFloor;【参考方案2】:

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
    

【讨论】:

amountTypedString 定义在哪里?【参考方案3】:

这是我在 Swift 中的解决方案。我的方法是跟踪输入的数字并将其乘以 0.01,并使用 NSNumberFormatter 将结果数字设置为 2 个小数点。请注意,我将文本字段键盘设置为数字键盘。

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

【讨论】:

除了提供代码外,还包括一些文本来识别OP问题的重要部分。【参考方案4】:

您可能需要查看UITextFieldDelegate 协议。通过将某些东西(通常是您的 ViewController 类)指定为 UITextField 的委托,您将能够从控件接收交互式文本输入事件。特别是:

- (BOOL)textField:(UITextField *)textField 
        shouldChangeCharactersInRange:(NSRange)range 
        replacementString:(NSString *)string

通过从该方法返回 NO,您可以阻止文本字段应用用户按下的键。

但是,即使您返回 NO,也没有什么可以阻止您使用传递给委托方法的信息来对代码中的文本字段内容进行自己的操作。

伪代码形式:

- (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;

免责声明:我还没有编写实际代码来测试,因此更改此委托方法中的代码可能会导致另一个 shouldChangeCharactersInRange: 消息触发。不过,我不相信情况会如此。我不相信当您更改代码中的文本时会触发委托事件。

这显然不如已经实现了数字文本输入框的所有规则的自定义控件(我现在回想起多年的 Visual Basic 编程......)。但我不知道有人编写和开源的任何此类控制。

【讨论】:

非常感谢您的回复,您给了我一些工作机会。再次感谢您的帮助! 别忘了处理if ([string length] == 0),表示用户按下了删除键。【参考方案5】:

我从 julien 那里得到了答案并对其进行了修改,以便支持具有不同货币格式的其他语言环境。我还存储了数字格式化程序以供​​重复使用,因此不会每次都创建它们,这是一项昂贵的操作。

通过忽略任何会导致存储值溢出的输入来处理溢出。

此解决方案假定文本仅在文本字段的末尾输入,因此无论文本光标在输入之前的位置如何,都只会在末尾操作。

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

在具有不同语言环境的操场上使用它给了我these results (抱歉,还不能直接嵌入)。

【讨论】:

以上是关于尝试将 UITextField 格式化为像货币计算器一样仅用于 iOS 中的数字输入的主要内容,如果未能解决你的问题,请参考以下文章

UITextField 显示格式为没有小数的货币

如何将 Double 格式化为货币 - Swift 3

如何将输出格式化为 UILabel 作为货币?

将数字格式化为货币金额

如何使用正则表达式将数字格式化为货币

PHP number_format():将数字四舍五入,然后格式化为货币