ios - 格式化货币 - Swift

标签 ios swift regex uitextfield currency-formatting

我正在寻找满足以下条件的文本字段货币格式化程序:

  1. 应该按照我输入的格式(逗号分隔)
  2. 应允许小数点前 10 位和小数点后 2 位
  3. 它应该允许 (2) 的正则表达式
  4. 当我们剪切时,光标应该保持在同一个地方
  5. 当我们在货币中间输入时,光标不应向左移动。
  6. 它应该支持正则表达式中的本地化(逗号和句点)。

我尝试了很多解决方案:

  1. 使用 NSCharacterSet(这是最接近的,但由于 . 的互换,此处正则表达式失败, 在本地化过程中,我们也在这里使用了 .decimal 类型来避免 textField 中的 $)

    class func checkTextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        guard let textBeforeEditing = textField.text else {
            return true
        }
        if ((string == "0" || string == "") && (textField.text! as NSString).range(of: ".").location < range.location) {
            return true
        }
        var currentPosition = 0
        if let selectedRange = textField.selectedTextRange {
            currentPosition = textField.offset(from: textField.beginningOfDocument, to: string == "" ? selectedRange.end : selectedRange.start)
        }
        let allowedCharacterSet = NSCharacterSet(charactersIn: "0123456789.").inverted
        let filtered = string.components(separatedBy: allowedCharacterSet)
        let component = filtered.joined(separator: "")
        let isNumeric = string.replacingOccurrences(of: ",", with: "") == component
        var textFieldString : String = ""
        var numberWithoutCommas : String = ""
        guard isNumeric else {
            return false
        }
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        textFieldString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
        numberWithoutCommas = textFieldString.replacingOccurrences(of: ",", with: "")
        let formattedNumberWithoutCommas = formatter.number(from: numberWithoutCommas)
        guard let formattedNumber = formattedNumberWithoutCommas, var formattedString = formatter.string(from: formattedNumber) else {
            textField.text = nil
            return false
        }
        if string == "." && range.location == textField.text?.count {
            formattedString = formattedString.appending(".")
        }
        textField.text = formattedString
        currentPosition = getCursorPositionForTextField(string: string, cursorPosition: currentPosition, formattedString: formattedString, textBeforeEditing: textBeforeEditing)
        handleTextFieldCursor(cursorPosition: currentPosition, textField: textField)
        return false
    }
    
  2. 使用 NumberFormatter 但每次剪切/粘贴时光标都会移动到结尾

    extension String {
    
        func currencyInputFormatting() -> String {
    
            var number: NSNumber!
            let formatter = NumberFormatter()
            formatter.numberStyle = .currency
            formatter.maximumFractionDigits = 2
            formatter.minimumFractionDigits = 2
    
            var amountWithPrefix = self
    
            // remove from String: "$", ".", ","
            let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
            amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.characters.count), withTemplate: "")
    
            let double = (amountWithPrefix as NSString).doubleValue
            number = NSNumber(value: (double / 100))
    
            guard number != 0 as NSNumber else {
                return ""
            }
    
            return formatter.string(from: number)!
        }
    }
    

我花了将近一两天的时间寻找 100% 可行的解决方案,但无法解决。 任何帮助将不胜感激

编辑

@denis_lor 答案的帮助下,我已经非常接近解决方案,但仍然无法实现逗号与句点的互换。这是我更新的代码,我错过了什么吗?它适用于英语,但不适用于西类牙语。

class func checkTextField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    guard let textBeforeEditing = textField.text else {
        return true
    }
    if ((string == "0" || string == "") && (textField.text! as NSString).range(of: "\(NSLocalizedString("core_decimal_separator_symbol", comment: ""))").location < range.location) {
        return true
    }
    var currentPosition = 0
    if let selectedRange = textField.selectedTextRange {
        currentPosition = textField.offset(from: textField.beginningOfDocument, to: string == "" ? selectedRange.end : selectedRange.start)
    }
    let allowedCharacterSet = NSCharacterSet(charactersIn: "0123456789\(NSLocalizedString("core_decimal_separator_symbol", comment: ""))").inverted
    let filtered = string.components(separatedBy: allowedCharacterSet)
    let component = filtered.joined(separator: "")
    let isNumeric = string.replacingOccurrences(of: NSLocalizedString("core_thousand_separator_symbol", comment: ""), with: "") == component
    var textFieldString : String = ""
    var numberWithoutCommas : String = ""
    guard isNumeric else {
        return false
    }
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    textFieldString = (textField.text! as NSString).replacingCharacters(in: range, with: string)
    numberWithoutCommas = textFieldString.replacingOccurrences(of: NSLocalizedString("core_thousand_separator_symbol", comment: ""), with: "")
    let formattedNumberWithoutCommas = formatter.number(from: numberWithoutCommas)
    guard let formattedNumber = formattedNumberWithoutCommas, var formattedString = formatter.string(from: formattedNumber) else {
        textField.text = nil
        return false
    }
    if string == NSLocalizedString("core_decimal_separator_symbol", comment: "") && range.location == textField.text?.count {
        formattedString = formattedString.appending(NSLocalizedString("core_decimal_separator_symbol", comment: ""))
    }
    textField.text = formattedString
    currentPosition = getCursorPositionForTextField(string: string, cursorPosition: currentPosition, formattedString: formattedString, textBeforeEditing: textBeforeEditing)
    handleTextFieldCursor(cursorPosition: currentPosition, textField: textField)
    return false
}

最佳答案

好吧,看起来你的顾虑可以通过第一轮实现你的第一个解决方案来解决,你只需要考虑 , 的本地化。和 . .这很简单,您可以通过多种不同的方式实现它,但重要的是您的应用程序已本地化,例如使用两种使用不同符号处理小数和千位的语言(假设这些语言是英语和意大利语) ):

  • [en] 语言使用 , 处理小数点分隔和成千上万的 .
  • [it] 语言用 . 处理小数点分隔和成千上万的 ,

A) 你可以做的是创建一个 Localizable.strings文件,然后以英语和意大利语为例本地化您的项目。为此,请在此处添加语言。 enter image description here

B) 然后转到您的 Localizable.strings文件和 localize它适用于您支持的语言(例如英语和意大利语),就像这张为德语和英语所做的图像 enter image description here

现在您将得到两个 Localizable.strings,一个用于英语,一个用于意大利语:

Localizable.strings(英文)

core_decimal_separator_symbol = ",";
core_thousand_separator_symbol = ".";

Localizable.strings(意大利语)

core_decimal_separator_symbol = ".";
core_thousand_separator_symbol = ",";

C) 在您的代码中,您需要处理的任何地方,例如您的小数点分隔符,您可以这样做,而不是硬编码:

removeDecimalSeparator = numberAsString.replacingOccurrences(of: NSLocalizedString("core_decimal_separator_symbol", comment: ""), with: "")

因此,每当您的应用本地化为英语时,例如,此代码将转换为:

removeDecimalSeparator = numberAsString.replacingOccurrences(of: ",", with: "")

例如,当您的应用本地化为意大利语时,此代码将转换为:

removeDecimalSeparator = numberAsString.replacingOccurrences(of: ".", with: "")

总结:将这些视为示例,同时考虑到我们在此答案中的 Localizable.strings。只是为了向您展示如何通过在您的应用中使用本地化以不同的方式针对不同的语言处理一些符号。

关于ios - 格式化货币 - Swift,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55745469/

相关文章:

ios - 当 UIScrollView 在 ViewController 上显示时 UIButton 不显示

SwiftUI - 设置背景颜色时大型导航栏被破坏

swift - 在应用重新启动时保留简单的文本文件

regex - 用 htaccess (php) 重写 URL

iOS:应用程序主页按钮快捷方式

objective-c - 手动旋转方法用侧开关打破

ios - 如何指定特定的 UIScrollView 滚动到顶部

arrays - 字典保存错误 "SwiftValue encodeWithCoder:]: unrecognized selector sent to instance"

regex - 如何使用 ProxyMatch 或 ProxyPassMatch 与 Regex 映射多个上下文路径

javascript 正则表达式 帮助 Latex