1

i have a text field delegate method my use cases are :-

  1. if (.) dot is not there limit input to two characters because 100% should not be discount.

  2. if user input is 1.99 how can update the delegate method that it should take two numbers after (.)

  3. same if input is 12.99 how can update the delegate method that it should take two numbers after (.)

 func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            
            guard !string.isEmpty else {
                return true
            }
            
            // maximum limit of input 2 as 100% should not be discount.
            if !textField.text!.contains(".") {
                let maxLength = 2
                let currentString: NSString = (textField.text ?? "") as NSString
                let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
                return newString.length <= maxLength

            } else {
                 // if there is (.)
                 let maxLength = 3
                let currentString: NSString = (textField.text ?? "") as NSString
                let newString: NSString = currentString.replacingCharacters(in: range, with: string) as NSString
                return newString.length <= maxLength
            }
            
        }
Wahid Tariq
  • 159
  • 10

2 Answers2

1

One option is to keep track of number of digits before and after a decimal point and decide if you have reached your threshold.

There is nothing wrong with your answer, but here is an alternative option if you like. You could use regex as sometimes keeping track of multiple bools, counts, variables might have some edge cases.

My REGEX skills are not too strong but here is a nice SO thread to get a REGEX for your problem and you might be able to optimize better than the REGEX I used below.

private func isValidDecimal(_ text: String) -> Bool {
    
    // ^ $ - Anchors to analyze the text as one chunk / block
    // \d{1,2}) - Match a string that has 1 or 2 characters
    // | - OR
    // \d{1,2}\.{1}(\d{1,2})?
    // - \d{1,2} - Match a string that has 1 or 2 characters
    // - \.{1} - followed by a max of 1 decimal
    // - (\d{1,2})? - followed by 0 - 2 characters after the decimal
    let pattern = "^(\\d{1,2}\\.{1}(\\d{1,2})?|\\d{1,2})$"
    
    do
    {
        let regex = try NSRegularExpression(pattern: pattern)
        
        let matches = regex.matches(in: text,
                                    options: NSRegularExpression.MatchingOptions(),
                                    range: NSRange(location: 0,
                                                   length: text.count))
        
        // We only want 1 match as more than one match
        // would mean error
        return matches.count == 1
    }
    catch
    {
        // handle errors
        print("error")
    }
    
    return false
}
extension TextHighlightVC: UITextFieldDelegate {
    
    func textField(_ textField: UITextField,
                   shouldChangeCharactersIn range: NSRange,
                   replacementString string: String) -> Bool {
        
        guard let text = textField.text else { return false }
        
        return isValidDecimal(text + string)
    }
}

Do note that this might not be the optimal REGEX pattern and it is just to show you an option.

Here is the result which seems to work for all your use cases

REGEX number of digits decimal places swift textfield iOS UITextFieldDelegate

Update based on Omar's comment:

one question if I press .5 at starting, textfield should show 0.5 how to achieve this using same regex

I think rather than handling this using regex, we could handle this as an edge case as this would seem easier to me.

We check if the first character entered is a decimal and add the 0 then.

Here is what I would do:

extension TextHighlightVC: UITextFieldDelegate {
    
    func textField(_ textField: UITextField,
                   shouldChangeCharactersIn range: NSRange,
                   replacementString string: String) -> Bool {
        
        guard let text = textField.text else { return false }
        
        // Check if the first character entered is a decimal
        if text.count == 0 && string == "." {
            
            // Add 0 to the text field
            textField.text = "0"
            return true
        }
        
        return isValidDecimal(text + string)
    }
}
Shawn Frank
  • 4,381
  • 2
  • 19
  • 29
0

this is the answer

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            guard !string.isEmpty else {
                return true
            }
            let newText = (textField.text! as NSString).replacingCharacters(in: range, with: string) as String
            if let num = Double(newText), num >= -1 && num <= 100 {
                return true
            } else {
                return false
            }
    
        }
Wahid Tariq
  • 159
  • 10