Objective c 自定义NSTextView insertText:replacementRange中断拼写检查

Objective c 自定义NSTextView insertText:replacementRange中断拼写检查,objective-c,macos,cocoa,spell-checking,nstextview,Objective C,Macos,Cocoa,Spell Checking,Nstextview,我有一个自定义的NSTextView子类,还有一个自定义的NSTextStorage组件。NSTextStorage根据上下文修改用户输入的文本 因为最终文本可能比用户最初输入的文本短,所以我必须在NSTextView中重写insertText:replacementRange。最起码的例子是: - (void) insertText:(id)string replacementRange:(NSRange)replacementRange { if ([self hasMarkedTe

我有一个自定义的NSTextView子类,还有一个自定义的NSTextStorage组件。NSTextStorage根据上下文修改用户输入的文本

因为最终文本可能比用户最初输入的文本短,所以我必须在NSTextView中重写
insertText:replacementRange
。最起码的例子是:

- (void) insertText:(id)string replacementRange:(NSRange)replacementRange {
    if ([self hasMarkedText]) {
        [[self textStorage] replaceCharactersInRange:[self markedRange] withString:string];
    } else {
        [[self textStorage] replaceCharactersInRange:[self selectedRange] withString:string];
    }

    [self didChangeText];
}
这在几个月的广泛测试中效果良好。。。。除了禁用自动拼写检查和更正之外。除非我停止打字,移动鼠标,在我的应用程序之间切换焦点,否则“曲线”不会出现在拼写错误的单词下。几秒钟后,整个文本视图被拼写检查。因为它发生在事实之后,自动更正当然被禁用

如果禁用自定义的
insertText:replacementRange:
方法,其他一切都会正常工作,自动拼写功能也会返回。我只需要小心不要触发导致文本缩短的更改,因为它会触发属性超出范围的错误(这是我自定义方法的最初原因)

显然,苹果的
insertText:replacementRange:
实现比我的要多得多。我在
[self-checkTextInRange…]
[self-checkTextInSelection:][/code>等上尝试了多种变体。它们都无法恢复正常功能

搜索苹果的文档并不能帮助我找到我在方法中遗漏的导致拼写检查中断的内容。任何建议或想法都将不胜感激

提前谢谢

编辑:以下是我的NSTextStorage提供的各种行为的示例。(
|
表示插入插入插入符号)

首先是:

* item
* |
如果我按回车键,我将得到以下结果(删除
*
):

另一个示例,如果启用了“更改跟踪”:

this is thee| time
如果我点击删除:

this is the|{--e--} time
如您所见,一次按键可能会导致文本中多个字符的添加或删除


编辑2:仅供参考--如果在文档末尾按return键时出现缩短,则会出现属性超出范围的问题--NSTextview尝试设置新段落样式,但发现文档比预期短。我找不到改变NSTextview目标范围的方法。

我有一个部分解决方案

在我的自定义
insertText:replacementRange:
方法中,在
didChangeText
之前:

NSinteger wordCount;
NSOrthography * orthography;

static NSInteger theWordCount;
NSOrthography  * orthography;

NSRange spellingRange = <range to check>

NSArray * results = [[NSSpellChecker sharedSpellChecker] checkString:[[self textStorage] string]
                                                               range:spellingRange
                                                               types:[self enabledTextCheckingTypes]
                                                             options:NULL
                                              inSpellDocumentWithTag:0
                                                         orthography:&orthography
                                                           wordCount:&theWordCount];
if (results.count) {
    [self handleTextCheckingResults:results forRange:spellingRange types:[self enabledTextCheckingTypes] options:@{} orthography:orthography wordCount:theWordCount];
}
NSinteger字数;
正字法;
静态输入字数;
正字法;
NSRange spellingRange=
NSArray*结果=[[NSSpellChecker SharedPellChecker]检查字符串:[[self-textStorage]字符串]
射程:斯佩林根
类型:[自启用的TextCheckingTypes]
选项:空
inSpellDocumentWithTag:0
正字法:&正字法
wordCount:&theWordCount];
if(results.count){
[self-HandleteTextCheckingResults:Range:spellingRange类型的结果:[self-enabledTextCheckingTypes]选项:@{}正字法:正字法字数:字数];
}
但是,这是不完整的:

  • 拼写检查和语法检查工作正常
  • 自动拼写更正和文本替换不起作用(即使启用)
(2018-05-30编辑)

更新回复(2018-05-22):

这个问题又出现了,我真的需要弄清楚

  • 我的自定义NSTextStorage与前面描述的基本相同,并且仍然可以工作

  • 我在NSTextView上使用了一个自定义的
    insertText:replacementRange:
    ,但它调用了
    [super insertText:replacementRange:
    ,以利用Apple的幕后工作,使拼写等更好地工作。我的自定义方法只需要设置一个布尔值

  • 在缩短文本时,我仍然会收到苹果公司的
    insertText:replacementRange:
    对文本中不存在部分的属性的请求。以前,我会被困在这里,因为我所尝试的一切要么导致崩溃,要么导致苹果的代码无限期地重复请求不存在的属性

  • 最后,我尝试使用空范围指针返回假属性,这似乎让苹果的代码感到高兴:

    - (NSDictionary *) attributesAtIndex:(NSUInteger)location effectiveRange:(nullable NSRangePointer)range {
        if (location > _backingAttributes.length) {
            // This happens if we shrink the text before the textview is aware of it.
            // For example, if we expand "longtext" -> "short" in our smart string, then
            // The textview may set and request attributes past the end of our
            // _backing string.
            // Initially this was due to error in my code, but now I had to add
            // This error checking back
            NSLog(@"get attributes at (%lu) in (%lu)", (unsigned long)location, (unsigned long)_backingAttributes.length);
            NSLog(@"error");
    
            // Apparently returning fake attributes satisfies [NSTextView insertText:replacementRange:]
            range = NULL;
            return  @{
                      NSForegroundColorAttributeName : [BIColor redColor],
                      NSFontAttributeName : [BIFont fontWithName:@"Helvetica" size:14.0]
                      };
    
        } else {
            return [_backingAttributes attributesAtIndex:location effectiveRange:range];
        }
    }
    
  • 经过进一步测试,结果证明这还不够。最后,我将以下内容添加到setter中,以存储macOS试图设置的无效属性和范围:

    - (void) setAttributes:(NSDictionary<NSString *,id> *)attrs range:(NSRange)range {
        if (NSMaxRange(range) > _backingAttributes.length) {
            _invalidAttrs = attrs;
            _invalidRange = range;
        } else {
            [self beginEditing];
            [_backingAttributes setAttributes:attrs range:range];
            [self edited:NSTextStorageEditedAttributes range:range changeInLength:0];
            [self endEditing];
        }
    }
    

  • 这似乎适用于以前可能触发异常或无限循环的各种情况。

    如何修改文本存储?您是否调用任何
    开始编辑
    结束编辑
    编辑:范围:更改长度:
    或其他内容?是的--所有三种。NSTextStorage中的所有东西都能完美工作(至少在我使用的几个月内)。我能找到的唯一问题是拼写问题,如上所述已修复——禁用自定义
    insertText:replacementRange:
    (但仍使用我的自定义NSTextStorage)和拼写返回。显然,您无法通过替换
    insertText:replacementRange:
    来修复属性超出范围的错误。错误来自何处?键入时的一种可能行为是,按回车键实际上可以删除插入点之前的字符,导致字符串比用户按enter键时短。在苹果的
    insertText…
    方法的某个时候,它请求attr
    - (void) setAttributes:(NSDictionary<NSString *,id> *)attrs range:(NSRange)range {
        if (NSMaxRange(range) > _backingAttributes.length) {
            _invalidAttrs = attrs;
            _invalidRange = range;
        } else {
            [self beginEditing];
            [_backingAttributes setAttributes:attrs range:range];
            [self edited:NSTextStorageEditedAttributes range:range changeInLength:0];
            [self endEditing];
        }
    }
    
    // Apparently returning fake attributes satisfies [NSTextView insertText]
    *range = _invalidRange;
    return _invalidAttrs;