如何使用Swift在文本字段(从右到左)输入货币格式?

时间:2015-04-21 20:57:46

标签: ios swift uitextfield swift3 nsnumberformatter

我有一个数字,我想说0.00

  • 当用户点击1.我们应该0.01
  • 当用户点击2.我们应该显示0.12
  • 当用户点按3.我们应该显示1.23
  • 当用户点击4.我们应该显示12.34

我怎么能用Swift做到这一点?

9 个答案:

答案 0 :(得分:72)

Swift 3 。在文本字段上输入货币格式(从右到左)

override func viewDidLoad() {
    super.viewDidLoad()

    textField.addTarget(self, action: #selector(myTextFieldDidChange), for: .editingChanged)
}

func myTextFieldDidChange(_ textField: UITextField) {

    if let amountString = textField.text?.currencyInputFormatting() {
        textField.text = amountString
    }
}

extension String {

    // formatting text for currency textField
    func currencyInputFormatting() -> String {

        var number: NSNumber!
        let formatter = NumberFormatter()
        formatter.numberStyle = .currencyAccounting
        formatter.currencySymbol = "$"
        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))

        // if first number is 0 or all numbers were deleted
        guard number != 0 as NSNumber else {
            return ""
        }

        return formatter.string(from: number)!
    }
}

答案 1 :(得分:23)

您可以创建一个子类化UITextField的货币文本字段。为UIControlEvents .editingChanged添加目标。添加选择器方法以过滤文本字符串中的数字。过滤掉字符串中的所有非数字后,您可以使用NumberFormatter再次格式化您的数字,如下所示:

Swift 4.2或更高版本

class CurrencyField: UITextField {
    var string: String { return text ?? "" }
    var decimal: Decimal {
        return string.decimal /
            pow(10, Formatter.currency.maximumFractionDigits)
    }
    var decimalNumber: NSDecimalNumber { return decimal.number }
    var doubleValue: Double { return decimalNumber.doubleValue }
    var integerValue: Int { return decimalNumber.intValue   }
    let maximum: Decimal = 999_999_999.99
    private var lastValue: String?
    override func willMove(toSuperview newSuperview: UIView?) {
        // you can make it a fixed locale currency if needed
        // Formatter.currency.locale = Locale(identifier: "pt_BR") // or "en_US", "fr_FR", etc
        addTarget(self, action: #selector(editingChanged), for: .editingChanged)
        keyboardType = .numberPad
        textAlignment = .right
        editingChanged(self)
    }
    override func deleteBackward() {
        text = string.digits.dropLast().string
        editingChanged(self)
    }
    @objc func editingChanged(_ textField: UITextField) {
        guard decimal <= maximum else {
            text = lastValue
            return
        }
        text = Formatter.currency.string(for: decimal)
        lastValue = text
    }
}

extension NumberFormatter {
    convenience init(numberStyle: Style) {
        self.init()
        self.numberStyle = numberStyle
    }
}
extension Formatter {
    static let currency = NumberFormatter(numberStyle: .currency)
}
extension String {
    var digits: String { return filter(("0"..."9").contains) }
    var decimal: Decimal { return Decimal(string: digits) ?? 0 }
}
extension Decimal {
    var number: NSDecimalNumber { return NSDecimalNumber(decimal: self) }
}

答案 2 :(得分:6)

我从Leo Dabus的答案开始(这对我来说不是开箱即用的),并且在尝试简化并使其正常工作的过程中最终得到了这个,我认为如果我精简和干净的话自己也这么说?

class CurrencyTextField: UITextField {

    /// The numbers that have been entered in the text field
    private var enteredNumbers = ""

    private var didBackspace = false

    var locale: Locale = .current

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }

    private func commonInit() {
        addTarget(self, action: #selector(editingChanged), for: .editingChanged)
    }

    override func deleteBackward() {
        enteredNumbers = String(enteredNumbers.dropLast())
        text = enteredNumbers.asCurrency(locale: locale)
        // Call super so that the .editingChanged event gets fired, but we need to handle it differently, so we set the `didBackspace` flag first
        didBackspace = true
        super.deleteBackward()
    }

    @objc func editingChanged() {
        defer {
            didBackspace = false
            text = enteredNumbers.asCurrency(locale: locale)
        }

        guard didBackspace == false else { return }

        if let lastEnteredCharacter = text?.last, lastEnteredCharacter.isNumber {
            enteredNumbers.append(lastEnteredCharacter)
        }
    }
}

private extension Formatter {
    static let currency: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        return formatter
    }()
}

private extension String {
    func asCurrency(locale: Locale) -> String? {
        Formatter.currency.locale = locale
        if self.isEmpty {
            return Formatter.currency.string(from: NSNumber(value: 0))
        } else {
            return Formatter.currency.string(from: NSNumber(value: (Double(self) ?? 0) / 100))
        }
    }
}

答案 3 :(得分:1)

试试这段代码:

struct DotNum {
  private var fraction:String = ""
  private var intval:String = ""
  init() {}
  mutating func enter(s:String) {
    if count(fraction) < 2 {
      fraction = s + fraction
    } else {
      intval = s + intval
    }
  }
  private var sFract:String {
    if count(fraction) == 0 { return "00" }
    if count(fraction) == 1 { return "0\(fraction)" }
    return fraction
  }
  var stringVal:String {
    if intval == ""  { return "0.\(sFract)" }
    return "\(intval).\(sFract)"
  }
}
var val = DotNum()
val.enter("1")
val.stringVal
val.enter("2")
val.stringVal
val.enter("3")
val.stringVal
val.enter("4")
val.stringVal

答案 4 :(得分:1)

我的最终代码感谢您的帮助

extension Double {
            var twoDigits: Double {
                let nf = NSNumberFormatter()
                nf.numberStyle = NSNumberFormatterStyle.DecimalStyle
                nf.minimumFractionDigits = 2
                nf.maximumFractionDigits = 2
                return self
            }
    }
    var cleanText:String!
            let number:String = sender.currentTitle as String!
            if(amountDisplay.text != nil)
            {
                cleanText = String(Array(amountDisplay.text!).map{String($0)}.filter{ $0.toInt() != nil }.map{Character($0)} ) as String
                cleanText = cleanText + number
            }else{
                cleanText = number
            }

            amount = (Double(cleanText.toInt()!) / 100).twoDigits
            formatter.locale = NSLocale(localeIdentifier: currencies[current_currency_index])
            amountDisplay.text = "\(formatter.stringFromNumber(amount!)!)"

答案 5 :(得分:0)

以下是swift 2的代码

@IBOutlet weak var txtAmount: UITextField!

//MARK: - UITextField Delegate -
    func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool{

        if string.characters.count == 0 {
            return true
        }

        let userEnteredString = textField.text ?? ""
        var newString = (userEnteredString as NSString).stringByReplacingCharactersInRange(range, withString: string) as NSString
        newString = newString.stringByReplacingOccurrencesOfString(".", withString: "")

        let centAmount : NSInteger = newString.integerValue
        let amount = (Double(centAmount) / 100.0)

        if newString.length < 16 {
            let str = String(format: "%0.2f", arguments: [amount])
            txtAmount.text = str
        }

        return false //return false for exact out put
    }

注意:从故事板或编程方式连接textField的委托

答案 6 :(得分:0)

只是为了好玩:将Thomas's answer(完整的积分 - 并请点给他)复制到一个文件中,作为Swift 4.1脚本运行(带有小修复):

<强> dotnum.swift:

#!/usr/bin/swift

struct DotNum {
    private var fraction:String = ""
    private var intval:String = ""
    init() {}
    mutating func enter(_ s:String) {
        if fraction.count < 2 {
          fraction = s + fraction
        } else {
          intval = s + intval
        }
    }
    private var sFract:String {
        if fraction.count == 0 { return "00" }
        if fraction.count == 1 { return "0\(fraction)" }
        return fraction
    }
    var stringVal:String {
        if intval == ""  { return "0.\(sFract)" }
        return "\(intval).\(sFract)"
    }
}

var val = DotNum()
val.enter("1")
print(val.stringVal)
val.enter("2")
print(val.stringVal)
val.enter("3")
print(val.stringVal)
val.enter("4")
print(val.stringVal)

然后在终端中运行:

$ chmod +x dotnum.swift
$ ./dotnum.swift
0.01
0.21
3.21
43.21

答案 7 :(得分:0)

感谢这里的每个人。从这里的所有答案中,我终于找到了我的答案。

首先,我将textField的初始值设置为:

private func commonInit() { 
    amountTextField.text = "0.00"
}

然后我使用UITextFieldDelegate来获取输入值和当前的textview.text:

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    //Need to check if the textfield.text can be evaluated as number or not before passing it to the function
    //Get the current text value, and current user input and pass it to the 
    let formattedAmount = formatAmount(oldAmount: textField.text, userInput: string)
    textField.text = formattedAmount
    return false
}

这里是我的私有函数,用于格式化数字以从右向左移动:

private func formatAmount(currentText: String, userInput: String) -> String {
    let amount = currentText.components(separatedBy: ".")
    var intValue: String = amount[0]
    var decimalValue: String = amount[1]
    

    //backspace registered, need to move the number to the right
    if userInput.isEmpty {
        decimalValue.remove(at: decimalValue.index(before: decimalValue.endIndex))
        decimalValue = intValue.last!.string + decimalValue
        intValue.remove(at: intValue.index(before: intValue.endIndex))
        if intValue.isEmpty {
            intValue = "0"
        }
    } else {
        
        //Need to consider if user paste value
        if userInput.count > 2 {
            decimalValue = String(userInput.suffix(2))
            intValue = String(userInput.dropLast(2))
        } else {
            decimalValue = rmAmount[1] + userInput
            
            //Add to int value (move to the right)
            intValue = intValue + decimalValue.first!.string
            
            if Int(intValue) == 0 {
                intValue = "0"      //00 -> 0
            } else if intValue.first == "0" {
                //remove 0 from at the first position in intValue
                intValue.remove(at: intValue.startIndex)    //01 -> 1
            }
            
            //Remove tenth place from decimal value since it goes to Int already
            decimalValue.remove(at: decimalValue.startIndex)
        }
    }
    return intValue + "." + decimalValue
}

基本上就是这样。您可以根据自己的计划添加其他额外的实现。让我知道我的实现是否有问题。

PS:这当然仅适用于某些货币,在我的情况下,我的应用程序仅针对该本地设置,这就是为什么我使用这种方式。

答案 8 :(得分:0)

经过反复尝试并给出了建议的答案,我找到了一个非常简单的解决方案:

textField的设置需要在视图的设置中调用。

在switch语句中,如果用户输入0到9之间的数字,则该数字将添加到先前的字符串值中。默认情况下会覆盖退格按钮,并从字符串中删除最后一个字符。

numberFormatter的语言环境设置为当前语言环境,因此可以使用不同的货币。

func setupTextField() {
        textField.delegate = self
        textField.tintColor = .clear
        textField.keyboardType = .numberPad
}


func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    setFormattedAmount(string)
    
    return false
}

private func setFormattedAmount(_ string: String) {
    switch string {
    case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
        amountString = amountString + string
    default:
        if amountString.count > 0 {
            amountString.removeLast()
        }
    }
    
    let amount = (NSString(string: amountString).doubleValue) / 100
    textField.text = formatAmount(amount)
}

private func formatAmount(_ amount: Double) -> String {
    let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    formatter.locale = .current
    
    if let amount = formatter.string(from: NSNumber(value: amount)) {
        return amount
    }
    
    return ""
}