2

To force a UITextfield for lowercase only I do the following:

lazy var myTextField: UITextField = {
    let textField = UITextField()
    textField.addTarget(self, action: #selector(forceLettersLowercased(_ :)), for: .editingChanged)
    return textField
}()

@objc func forceLettersLowercased(_ sender: UITextField) {
    sender.text = sender.text?.lowercased()
}

How can I achieve the same thing with a UITextView?

lazy var myTextView: UITextView = {
    let textView = UITextView()
    return textView
}()

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
      
    let textLowerCased = text.lowercased()

    textView.text = (textView.text as NSString).replacingCharacters(in: range, with: textLowerCased)

    let numberOfChars = textView.text.count
    
    return numberOfChars <= 200
}

I keep getting double letters, like when I type the letter 'W' I get a small one and a capital one, then when I press backspace I get: [__NSCFString replaceCharactersInRange:withString:]: Range or index out of bounds

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • Weird, I was trying to figure out why you got index out of range, but I can't actually reproduce the error... – Sweeper Feb 13 '21 at 02:29
  • I have no idea. I'm working on another bug. After I finish I'll get back to this, reproduce it again, and then explain to you exactly what steps I took. – Lance Samaria Feb 13 '21 at 03:55

2 Answers2

2

You are overriding the wrong delegate method. shouldChangeTextIn, as its name suggests, is a delegate method that asks you whether the text in the given range should get changed. You are supposed to use this method to, e.g. prevent users from entering certain characters.

It just so happens to be called whenever the text view's text is changed, but that doesn't mean you should use it to detect the text being changed in a text view, because the method will get called before the text is changed, which makes sense: it asks you whether the text should be changed, before actually changing it. That's why there are two Ws. The first lowercase w came from you setting the text view's text, and the second uppercase W came from the user actually typing it.

The delegate method that you should implement is:

func textViewDidChange(_ textView: UITextView)

This one actually gets called after the text has been changed. It is here that you can do the same thing that you did as the UITextField:

textView.text = textView.text?.lowercased()

You seem to also be limiting the max length of the text view to 200 characters. That functionality should stay in shouldChangeTextIn. See this post for how to do this if you are unsure.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

Instead of shouldChangeTextIn, use textViewDidChange.

extension ViewController: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        textView.text = textView.text.lowercased()
    }
}

Result:

Typing while keyboard caps lock is on, but the text entered turns out all lowercased

aheze
  • 24,434
  • 8
  • 68
  • 125