0

Basically I would like to limit the user to enter only ONE DECIMAL POINT in a UITextView object, I would also like to limit the number after the ONE DECIMAL POINT to TWO (i.e two decimal places numbers). I am using the below code which currently works in preventing the user from enter more than ONE DECIMAL POINT:

// The below extension basically counts the number of a specific character in a string:

extension String {

func countInstances(of stringToFind: String) -> Int {

    var stringToSearch = self

    var count = 0

    while let foundRange = stringToSearch.range(of: stringToFind, options: .diacriticInsensitive) {

        stringToSearch = stringToSearch.replacingCharacters(in: foundRange, with: "")

        count += 1

    }

    return count

}

}

class ViewController: UIViewController, UITextViewDelegate, UIPopoverPresentationControllerDelegate {
override func viewDidLoad() {

    super.viewDidLoad()

    self.indicativeDesignWorkingLifeTextView.delegate = self

    indicativeDesignWorkingLifeTextView.keyboardType = .decimalPad

}

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    let decimalCount = indicativeDesignWorkingLifeTextView.text.countInstances(of: ".")


    if decimalCount > 0 {

        return false

    } else {

        return true

    }

}

}

However, the problem with the above code is that it works in terms of limiting the user to enter only ONE DECIMAL POINT inside the UITextView but it also limit the user from doing anything after entering the decimal point. It does not even allow the user to delete the one decimal point that he entered and insert a new one or any other number, Basically as soon as one decimal point is entered the user cannot edit anything inside the UITextView. Any idea on how can I amend this? and also limit the user to enter only number to two decimal places?

  • 2
    Possible duplicate of [Limiting text field entry to only one decimal point](https://stackoverflow.com/questions/8076160/limiting-text-field-entry-to-only-one-decimal-point) – Abdelahad Darwish Aug 29 '17 at 19:22
  • Can you describe the allowed text a little more? The user can type a decimal number, correct? Are they supposed to be allowed to type anything other than a decimal number? – Tom Harrington Aug 29 '17 at 19:52
  • What I want is to allow the user to use the decimal pad keyboard to enter any number up to two decimal places (e.g. 55.55) and only to use one decimal point (e.g 55.55 is okay however 55..55 is not) – Shadi Hammoudeh Aug 29 '17 at 20:03
  • Why are you using text view instead of text field if you only want a single line? – Leo Dabus Aug 29 '17 at 21:29
  • please check `let decimalOccurences = textView.text.characters.filter { $0 == "." }.count` – Vini App Aug 30 '17 at 00:09

3 Answers3

0

Depending on if your use case permits it, I'd strongly urge you to use a supported iOS provided keyboardType so that you don't have to do all the work yourself.

    indicativeDesignWorkingLifeTextView.keyboardType = .decimalPad
mango
  • 5,577
  • 4
  • 29
  • 41
0

Your UITextViewDelegate method name is wrong. It has to be

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, 
   replacementText text: String) -> Bool
algrid
  • 5,600
  • 3
  • 34
  • 37
  • I am using a UITextView not a UITextField. I even copied and pasted what you wrote and replaced textField with indicativeDesignWorkingLifeTextView and UITextField with UITextView but I still cannot get it to work? – Shadi Hammoudeh Aug 29 '17 at 19:26
  • Fixed the func name for UITextViewDelegate – algrid Aug 29 '17 at 19:30
  • I just realized that I am using the same function that you mentioned above somewhere else (I updated my code to reflect this). How can I keep same function working? I tried to copy and paste the code for the first function into the second one but I received a warning stating that anything after the word RETURN will not be executed. What is the best workaround this? – Shadi Hammoudeh Aug 29 '17 at 19:42
  • Basically I want to use the same function twice but each have a different return value and the problem I am having now is that any code I write after the first return value is ignored? – Shadi Hammoudeh Aug 29 '17 at 20:04
  • You should have only one function - with correct name. And of course you shouldn't put any code after a return statement - it will never be executed. Remove that return statement if it's ok for you. For your task you can take a look here - https://stackoverflow.com/questions/10404067/how-can-i-limit-the-number-of-decimal-points-in-a-uitextfield It's not exactly what you need, but may useful. – algrid Aug 29 '17 at 20:16
0

You could evaluate the resulting text and check if there is more than one decimal point in it.

for example:

   if let newText = (indicativeDesignWorkingLifeTextView.text as NSString!)?
                    .replacingCharacters(in: range, with: text),
      newText.components(separatedBy:".").count > 2
   { return false }

For some reason, Apple decided to pass an old NSRange type for the substring rather than the Swift type (Range < String.Index >) requiring an unwieldy type cast to NSString!. The condition would have been much cleaner and legible otherwise.

If you want the whole condition to allow only decimal characters, with no more than one decimal point and max 2 digits after the decimal, you can combine it like this:

   if let newText = (textView.text as NSString!)?.replacingCharacters(in: range, with: text),
      case let parts = newText.components(separatedBy:"."),
      parts.count > 2 
      || text.characters.contains(where:{ !"01234567890.".characters.contains($0) })
      || parts.count == 2 && parts[1].characters.count > 2
   { return false }
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • Hi Alain, Could you please elaborate further please note that I am new to coding so could you please explain in more details how to implement what you suggested in my code? – Shadi Hammoudeh Aug 31 '17 at 17:36
  • Not sure what additional details you need, the second code snippet in my answer can essentially be place in your textView(... should changeTextIn...) function and will perform the validation you want. You just have to paste it there and make sure to use your own parameter names for the textView, range and replacement string. – Alain T. Aug 31 '17 at 19:15
  • I updated my code as per what you suggested (please refer to above code) However, I am getting an error next to the code line if let newTex = ... the error says "use of unresolved identifier text" any idea how can I get over this error? – Shadi Hammoudeh Aug 31 '17 at 22:10