Home > Net >  How to format numbers in a textField with math equation string?
How to format numbers in a textField with math equation string?

Time:07-18

I'm trying to format numbers in a UITextField consists of math equation string: "number number".

At the moment I can type just a single number, then convert it to Double -> format with NSNumberFormatter -> convert back to String -> assign to textField.text:

The code:

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

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

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.decimalSeparator, with: ".")
let completeString = correctDecimalString.replacingOccurrences(of: formatter.groupingSeparator, with: "")

guard let value = Double(completeString) else { return false }

let formattedNumber = formatter.string(for: value)
textField.text = formattedNumber

return string == formatter.decimalSeparator
}

Now I want to add a calculation functionality and display a simple math equation in a textField as "number number", but each number should be formatted as shown above. Example (but without formatting):

I can't properly implement that. The logic for me was: track the String each time new char inserts -> if it has math sign extract numbers -> convert them to Double -> format with NSNumberFormatter -> convert back to String -> construct a new String "number number".

The code I tried:

        if let firstString = completeString.split(separator: " ").first, let secondString = completeString.split(separator: " ").last {
        guard let firstValue = Double(firstString) else { return false }
        guard let secondValue = Double(secondString) else { return false }

        let firstFormattedNumber = formatter.string(for: firstValue)
        let secondFormattedNumber = formatter.string(for: secondValue)

        textField.text = "\(firstFormattedNumber ?? "")   \(secondFormattedNumber ?? "")"

    // another try
    if completeString.contains(" ") {
        let stringArray = completeString.components(separatedBy: " ")
        for character in stringArray {
            print(character)
            guard let value = Double(character) else { return false }
            guard let formattedNumber = formatter.string(for: value) else { return false }
            textField.text = "\(formattedNumber)   "
        }
    }

But it's not working properly. I tried to search but didn't find any similar questions.

Test project on GitHub

How can I format the numbers from such a string?

CodePudding user response:

Here is how I was able to solve my question:

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.maximumFractionDigits = 2
    formatter.locale = .current
    formatter.roundingMode = .down
    
    //set of possible math operations
    let symbolsSet = Set([" ","-","x","/"])

    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.decimalSeparator, with: ".")
    let completeString = correctDecimalString.replacingOccurrences(of: formatter.groupingSeparator, with: "")

    //receive math symbol user typed
    let symbol = symbolsSet.filter(completeString.contains).first ?? ""

    //receive number of symbols in a String. If user wants to type more than one math symbol - do not insert
    let amountOfSymbols = completeString.filter({String($0) == symbol}).count
    if amountOfSymbols > 1 { return false }

    //receive numbers typed by user
    let numbersArray = completeString.components(separatedBy: symbol)
    //check for each number - if user wants to type more than one decimal sign - do not insert
    for number in numbersArray {
        let amountOfDecimalSigns = number.filter({$0 == "."}).count
        if amountOfDecimalSigns > 1 { return false }
    }
    guard let firstNumber = Double(String(numbersArray.first ?? "0")) else { return true }
    guard let secondNumber = Double(String(numbersArray.last ?? "0")) else { return true }
    
    let firstFormattedNumber = formatter.string(for: firstNumber) ?? ""
    let secondFormattedNumber = formatter.string(for: secondNumber) ?? ""
    // if user typed math symbol - show 2 numbers and math symbol, if not - show just first typed number
    textField.text = completeString.contains(symbol) ? "\(firstFormattedNumber)\(symbol)\(secondFormattedNumber)" : "\(firstFormattedNumber)"
    
    return string == formatter.decimalSeparator
}
  • Related