I have a custom textField’s input view – it is a Numpad
style keyboard. Numpad
is using to add numbers and math symbols to a textField
.
I can’t figure out how can I change a math symbol in a string if user already add one and wants to change it on another straight away. Here is an example of what I need:
Here is the code I use:
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
}
The logic for me was to use textField.deleteBackwards()
method to delete an old one and add a new math symbol after, but with above code it doesn’t work: it deletes symbol, but a new one doesn’t appear – I should press again so new symbol can appear.
What should I do to change a math symbol in a string?
2
Answers
You should add a listener for your UITextView text change.
Inside the
shouldChangeCharactersIn
delegate use a logic as follows.There may be different ways of achieving the expected output.
This is another way you can achieve the above result.
Check the logic in both answers.