繁体   English   中英

如何替换 UITextField 中字符串中的特定字符?

[英]How to replace a specific character in a string inside a UITextField?

我有一个自定义 textField 的输入视图 - 它是一个Numpad风格的键盘。 Numpad用于将数字和数学符号添加到textField

如果用户已经添加了一个数学符号并想立即在另一个上更改它,我无法弄清楚如何更改字符串中的数学符号。 这是我需要的一个例子:

这是我使用的代码:

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

//number formatter
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.maximumFractionDigits = 2
formatter.locale = .current
formatter.roundingMode = .down

//all possible math operation symbols user can add
let symbolsSet = Set(["+","-","x","/"])
var amountOfSymbols = 0

let numberString = textField.text ?? ""
guard let range = Range(range, in: numberString) else { return false }
let updatedString = numberString.replacingCharacters(in: range, with: string)
let correctDecimalString = updatedString.replacingOccurrences(of: formatter.groupingSeparator, with: "")
let completeString = correctDecimalString.replacingOccurrences(of: formatter.decimalSeparator, with: ".")

//current math symbol user add
let symbol = symbolsSet.filter(completeString.contains).last ?? ""
//if user add math symbol to an empty string - do not insert
if string == symbol, numberString.count == 0 { return false }

//count how much math symbols string has. If more that one - do not insert, string can have only one
completeString.forEach { character in
    if symbolsSet.contains(String(character)) {
        amountOfSymbols += 1
    }
}
if amountOfSymbols > 1 { return false }

//count how much decimals string has. If more that one - do not insert because it can have only one per number
let numbersArray = completeString.components(separatedBy: symbol)
for number in numbersArray {
    let amountOfDecimalSigns = number.filter({$0 == "."}).count
    if amountOfDecimalSigns > 1 { return false }
}

//create numbers from a string
guard let firstNumber = Double(String(numbersArray.first ?? "0")) else { return true }
guard let secondNumber = Double(String(numbersArray.last ?? "0")) else { return true }

//format numbers and turn them back to string
let firstFormattedNumber = formatter.string(for: firstNumber) ?? ""
let secondFormattedNumber = formatter.string(for: secondNumber) ?? ""

//assign formatted numbers to a textField
textField.text = completeString.contains(symbol) ? "\(firstFormattedNumber)\(symbol)\(secondFormattedNumber)" : "\(firstFormattedNumber)"

return string == formatter.decimalSeparator
}

我的逻辑是使用textField.deleteBackwards()方法删除一个旧的并在之后添加一个新的数学符号,但是使用上面的代码它不起作用:它删除了符号,但没有出现一个新的符号 - 我应再次按下,以便出现新符号。

我应该怎么做才能更改字符串中的数学符号?

GitHub上的测试项目

更新:

我能得到的最接近的工作解决方案是:

     var completeString = correctDecimalString.replacingOccurrences(of: formatter.decimalSeparator, with: ".")

        //count how much math symbols string has. If more than one - do not insert, string can have only one
    completeString.forEach { character in
        if symbolsSet.contains(String(character)) {
            amountOfSymbols += 1
        }
    }
    
    if amountOfSymbols > 1 {
        let newSymbol = completeString.last?.description ?? ""
        completeString.removeAll { symbolsSet.contains(String($0))}
        textField.text = completeString+newSymbol
        return false
    }

但是当我添加第二个数字并再次按下数学符号时出现问题:

也许我的方法是错误的? 怎么做才对?

您应该为您的 UITextView 文本更改添加一个侦听器。

NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: myTextField, queue: OperationQueue.main) { [weak self] (notification) in
    guard let self = self else { return }
            
    guard let textField = notification.object as? UITextField else { return }
            
    //here write a logic which verifies if the last character in the text is one of the mathematical symbols, and the one previous to that is also a math symbol, then you would replace the 'lastIndex - 1' with the last character
    guard let text = textField.text, text.count > 2 else { return }
    guard let lastChar = text.last else { return }
    let previousChar: Character = Array(text)[text.count - 2]
    //compare these two characters and make sure the 'previousChar' is a math symbol but not a number, if so - replace it with 'lastChar'
}

shouldChangeCharactersIn委托中使用如下逻辑。

     
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
        var shouldChange : Bool = true
        
        //current text visible in the text field
        let numberString = textField.text ?? ""
        
        //last charachter in the text field
        let lastCharachter = numberString.last
        
        //get all the numbers displayed in the textfield by sperating them from symbols
        let numbersArray = numberString.components(separatedBy: CharacterSet(charactersIn: "+-x/"))
        
        
        if string == ""{
            
        }
        
        //stop entering a symbol in the first place
        else if numberString == ""{
            if !Character(string).isNumber{
                shouldChange = false
            }
        }
        
        //if a number is entered check for the decimal points
        else if Character(string).isNumber{
            if numbersArray.last!.contains(".") && numbersArray.last?.components(separatedBy: ".").last?.count == 2 && Character(string).isNumber{
                shouldChange =  false
            }
        // if symbol is entered
        }else{
            if string == "."{
                if !lastCharachter!.isNumber{
                    shouldChange = false
                }
            }

            //if there are more than 1 numbers numbersArray, calculate the value
            else if lastCharachter!.isNumber{
                if numbersArray.count > 1{
                    let expression = NSExpression(format: numberString)
                    let answer =  expression.expressionValue(with:nil, context: nil) as! Double
                    textField.text = "\(forTrailingZero(temp: answer))\(string)"
                    shouldChange = false
                }
            }else{
                //change the symbol
                textField.text = "\(textField.text!.dropLast())\(string)"
                shouldChange =  false
            }
        }
        
        return shouldChange
    }
    
    //use to remove trailing zeros
    func forTrailingZero(temp: Double) -> String {
        let tempVar = String(format: "%g", temp)
        return tempVar
    }

实现预期输出可能有不同的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM