Returning false in shouldChangeTextInRange method auto capitalize all letters
Asked Answered
C

2

14

I am seeing a strange issue with UITextView in latest iOS versions. As per my current understanding, it is only occurring in iOS 13.  One of my users reported this on iOS 12.4.1, though I am unable to reproduce this on any pre iOS 13 devices.

I have created a small sample project in Xcode 11 to explain the issue. The project is targeting iOS 13. I am testing on an iPhone 8 running iOS 13.1.2 (17A860). The project contains a simple view controller with a textview. The TextView has autoCapitalizationType as "Sentences". Then I implemented the delegate method shouldChangeTextInRange method. See the code below

class ViewController: UIViewController {  
    @IBOutlet weak var textView: UITextView!  
    override func viewDidLoad() {  
        super.viewDidLoad()  
        self.textView.delegate = self  
        self.textView.autocapitalizationType = .sentences  
    }  
}  
  
extension ViewController: UITextViewDelegate {  
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange,
      replacementText text: String) -> Bool {  
        //I wanted to do some other modifications here, but for now just
        //setting the user typed text manually and returning false  
        if let oldString = textView.text {  
            let newString = oldString.replacingCharacters(in: Range(range, in: oldString)!,  
                                                          with: text)  
            textView.text = newString  
        }  
        return false  
    }  
}  

This is the whole code, except the storyboard and other project files. As you can see above, I am just setting the exact same text entered by the user into the textview manually, and then return false. But when I type, the first character gets added in caps as it should. The second character correctly gets added in small letter. From third character onwards everything is in caps. Moreover, the keyboard shift button automatically getting selected after each character is typed. A screenshot is attached.

enter image description here

Some other important points.

  1. This issue is happening only if autocapitalizationType is "sentences", not for other values like "words", "none".
  2. I tried to set autocapitalizationType in code and in storyboard, with same results.
  3. If I don't implement shouldChangeTextInRange method, issue is not reproduced. If I return true from this method, issue is not reproduced. Since I need to do some processing in this method, I need to return false.
  4. Yet to reproduce by myself on a pre iOS 13 device, though a user reported this on iOS 12.4.1 (unverified for now).

Can anyone suggest a fix/workaround for this issue?  

Carisa answered 17/10, 2019 at 5:39 Comment(0)
M
9

The best workaround that I have found so far is to do the processing in textViewDidChange instead.

The bug is also discussed here: Apple developer forum

Muscadel answered 23/10, 2019 at 14:48 Comment(2)
Hmm, I also had to use the same workaround. This definitely looks like an Apple bug.Carisa
Bounty period over. Since no other solution was discussed and I also had to follow the same workaround, I am giving the bounty to you. This is definitely an Apple bug.Carisa
S
12

Try scheduling this assignment statement in a main loop:

    textView.text = newString

becomes:

    DispatchQueue.main.async {
        textView.text = newString
    }

This successfully worked around a similar issue for me where the first two letters of the sentence were being capitalized. My best guess is that the logic behind the didSet listener for the UITextView's text property changed in a way that immediately fires the delegate synchronously. Scheduling the assignment asynchronously on the main queue forces/guarantees the delegate returns before being fired that second time.

Slovene answered 5/11, 2019 at 21:48 Comment(2)
This will cause autocorrect suggestions to stop working.Circumscription
Using this in shouldChangeTextIn will cause new text to be added in incorrect places if the assignment is executed too late. But unless you are doing heavy processing it should be fine.Sizable
M
9

The best workaround that I have found so far is to do the processing in textViewDidChange instead.

The bug is also discussed here: Apple developer forum

Muscadel answered 23/10, 2019 at 14:48 Comment(2)
Hmm, I also had to use the same workaround. This definitely looks like an Apple bug.Carisa
Bounty period over. Since no other solution was discussed and I also had to follow the same workaround, I am giving the bounty to you. This is definitely an Apple bug.Carisa

© 2022 - 2024 — McMap. All rights reserved.