How can I programmatically dismiss the autocorrect

2019-02-11 09:16发布

I'm doing some custom auto-complete stuff on my own with insertText:, but if there's an autocorrect suggestion visible the view gets into a weird state.

If I use [textView unmarkText], it dismisses the autocorrect popup thingy -- but it accepts the autocorrection (which is bad). Is there some way to programmatically reject the autocorrect suggestion?

My current "solution" works, but it's gross and hacky and I have no reason to assume it will continue to work in the future. Is there a better way to do this?

- (void)dismissAutocorrectSuggestionForTextView:(UITextView *)textView {
    NSRange range = textView.selectedRange;
    textView.text = textView.text;
    textView.selectedRange = range;
}

4条回答
一夜七次
2楼-- · 2019-02-11 09:18

Your original solution is close. Try the following:

- (void)dismissAutocorrectSuggestionForTextView:(UITextView *)textView {
    NSRange rangeCopy = textView.selectedRange;
    NSString *textCopy = textView.text.copy;
    [textView resignFirstResponder];
    [textView becomeFirstResponder];
    [textView setText:textCopy];
    [textView setSelectedRange:rangeCopy];
}

Calling resign/become first responder back to back causes the text view to accept pending autocorrections immediately, but without actually dismissing the keyboard (try it you'll be surprised). This works on iOS 6 and iOS 7 for sure. After accepting the autocorrections, you then reset the text and the selected range to what they were prior to the autocorrections.

查看更多
Root(大扎)
3楼-- · 2019-02-11 09:38

I just encountered this issue myself. This solution works well:

[textView.inputDelegate textWillChange:textView];
[textView.inputDelegate textDidChange:textView];
查看更多
ら.Afraid
4楼-- · 2019-02-11 09:44

I tried something similar to yours, but setting the text of the textView this way results in the textView scrolling unnecessarily (my textView contains quite a bit of text). My solution involves restoring the contentOffset in an non-animated fashion. It's not exactly any more elegant than what you have, but at least it helps those who need to deal with longer text.

As for whether it'll continue to work in future, I've tried something like this since iOS 4, and it continues to work through iOS 6.

- (void)rejectAutoCorrectSuggestionInTextView:(UITextView *)textView
{
    if ([textView isFirstResponder])
    {
        NSString *original = textView.text;
        NSRange originalRange = textView.selectedRange;
        CGPoint originalOffset = textView.contentOffset;

        // Force any pending autocorrection to be applied
        [textView resignFirstResponder];
        [textView becomeFirstResponder];

        NSString *final = textView.text;

        if (![original isEqualToString:final])
        {
            textView.text = original;
            textView.selectedRange = originalRange;
            [textView setContentOffset:originalOffset animated:NO];
        }
    }
}
查看更多
老娘就宠你
5楼-- · 2019-02-11 09:44

If resigning first responder from text view is unwanted and you can attach as a delegate to the text view, you could implement method

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

and return NO in a specific case. For example, you could try the following:

@implementation
{
    BOOL _denyReplacements;
}

//////////////

- (void)dismissAutocorrectSuggestionForTextView:(UITextView *)textView {
    NSRange rangeCopy = textView.selectedRange;
    NSRange fakeRange = rangeCopy;
    fakeRange.location--;
    _denyReplacements = YES;
    [textView unmarkText];
    [textView setSelectedRange:fakeRange];
    [textView setSelectedRange:rangeCopy];
    _denyReplacements = NO;
}

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
     return !_denyReplacements;
}

When text view selection changes, autocompletion attempts to accept current suggestion, but text view consults this method before making replacements. An example of why would you do this instead of just resigning/becoming first responder is when you have some logic in textViewDidBeginEditing and/or textViewDidEndEditing methods, for example, which you don't want to be performed when you dismiss the autocorrection.

查看更多
登录 后发表回答