5
votes

I've run into a problem where iOS is giving my UITextViewDelegate incorrect information when the delete key is held on the keyboard.

When the user HOLDS the delete key on a UITextView on an iPad the UITextView will begin to delete entire words instead of individual characters the longer it is held down (note: this does not occur in the simulator).

When this happens, the UITextView delegate method:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text

Gets called with a range consisting of the correct cursor location, but a length of 1. This is incorrect as the UITextView is now deleting entire words, not single letters. The following code, for example, will print only a single space.

[textView substringWithRange:range]
string contains " "

Despite the UITextView removing a whole word. The replacement text is correctly given as the empty string. Does any one know of a solution or workaround to this problem?

1
Any luck with this simeon? Is there a way to prevent the "delete by word" action or something?typeoneerror
I still can't find a way to prevent it (or get it to submit the correct data to the delegate), but being able to detect it has allowed me to work around the bug.simeon
Note there's an additional bug with UITextViewDelegate. If you set General->Accessibility->Triple-click Home to "VoiceOver" or "Ask" in your settings, then UITextViewDelegates receives duplicate "shouldChangeTextInRange" messages for any punctuation characters. This was extremely hard to track down.simeon
Why do use both shouldChange and didChange if you only registering the change?Sulthan
Because shouldChange gives me the delta change, which allows quicker updating of the internal buffer. didChange would require me to analyse the text for differences.simeon

1 Answers

5
votes

Jacob mentioned I should post this as an answer. So here it is.

My hackish workaround to this is to monitor the text length and range given in shouldChangeTextInRange, and then compare it to the length of the text in textViewDidChange. If the differences are out of sync I flush my backing text buffer and rebuild it from the text view. This is not optimal. Here is my temporary workaround:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    //Push the proposed edit to the underlying buffer
    [self.editor.buffer changeTextInRange:range replacementText:text];

    //lastTextLength is an NSUInteger recording the length that
    //this proposed edit SHOULD make the text view have
    lastTextLength = [textView.text length] + ([text length] - range.length);

    return YES;
}

- (void)textViewDidChange:(UITextView *)textView
{
    //Check if the lastTextLength and actual text length went out of sync
    if( lastTextLength != [textView.text length] )
    {
        //Flush your internal buffer
        [self.editor.buffer loadText:textView.text];
    } 
}