James J
James J

Reputation: 6458

textView:shouldChangeTextInRange:replacementText: returning NO, but autocorrect ignores?

I'm having the following problem, and am not sure if this is an iOS bug, or I'm misunderstanding UITextViewDelegate callbacks. I'm able to reproduce this behavior on my device (iPad retina) and on the simulator only if I use the software keyboard (i.e. use my mouse to click the on-screen keyboard).

Consider the following contrived example project (Updated 1/9/14): https://www.dropbox.com/s/1q3vqfnsmmbhnuj/AutocorrectBug.zip

It's a simple UITextView, with a view controller set as its delegate, with the following delegate method:

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

    if ([text isEqualToString:@" "]
        && range.length == 0
        && (range.location == 0
            || [[textView.text substringWithRange:NSMakeRange(range.location-1, 1)] isEqualToString:@"\n"])) {
                textView.backgroundColor = [UIColor yellowColor];
                return NO;
    }
    return YES;
}

My intention with this code is that when the user types a space (edit: and that space is the first character on the line), the space is ignored and instead some other functionality happens, in this case the UITextView turns yellow.

Now consider these steps:

  1. Tap in the text view to make it first responder
  2. Type "Apple", hit return and note the positioning
  3. Now, type a space (turning the text view yellow), type "Apple" again and hit return.

Expected: A yellow background and text reading:

Apple
Apple

Observed: A yellow background and text reading (due to an autocorrection):

Apple
 Apple

It appears the autocorrection logic is ignoring the result of textView:shouldChangeTextInRange:replacementText:.

Edit 1/9/14: only the first space on a line should be ignored, further spaces should be processed as normal (i.e. inserted into the text), ruling out a brute force strip of spaces. Also I'm dealing with a large string (potentially hundreds of thousands of characters) in a text editor (meaning constant user typing), so analyzing the entire string with every keystroke isn't going to be performant.

Upvotes: 1

Views: 7912

Answers (1)

Joseph Chen
Joseph Chen

Reputation: 1530

Yes, I see it too on my iPad 7.0.3 simulator.

One way to solve it is to add this UITextViewDelegate method:

- (void)textViewDidChange:(UITextView *)textView
{
    // eliminates spaces, including those introduced by autocorrect
    if ([textView.text rangeOfCharacterFromSet:[NSCharacterSet whitespaceCharacterSet]].location != NSNotFound) {
        textView.text = [textView.text stringByReplacingOccurrencesOfString:@" " withString:@""];
    }
}

Update

It seems that this delegate method is called after autocorrection, just that the in the case you want to prevent (where "Apple" becomes " Apple") the replacement text is " Apple" and not " ". So to modify the implementation to prevent "\n " but allow other " " characters in your text, you could try comparing the first character of text to " " instead of comparing text to " ".

- (BOOL) textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
    
    // compare first character to 'space' character by decimal value
    if (text.length && [text characterAtIndex:0] == 32) {
        
        // check if previous character is a newline
        NSInteger locationOfPreviousCharacter = range.location - 1;
        if (locationOfPreviousCharacter < 0 || [textView.text characterAtIndex:locationOfPreviousCharacter] == 10) {
            textView.backgroundColor = [UIColor yellowColor];
            return NO;
        }
    }
    
    return YES;
}

Upvotes: 1

Related Questions