我有一个自定义的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:]
等。 它们都无法恢复正常功能。
搜索Apple的文档无助于指出我从方法中遗漏的内容,这些内容导致拼写检查中断。 任何指示或想法将不胜感激!!
提前感谢!
编辑:以下是我的NSTextStorage提供的各种行为的一些示例。(|
表示插入插入符号(
从以下开始:
* item
* |
如果我按下返回键,我最终会得到以下内容(删除*<space>
(:
* item
|
另一个示例,如果启用了"更改跟踪":
this is thee| time
如果我点击删除:
this is the|{--e--} time
如您所见,一次击键可能会导致在文本中添加或删除多个字符。
编辑 2:仅供参考 - 当在文档末尾按回车键时发生缩短时,我遇到的属性超出范围的问题 - 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];
}
但是,这是不完整的:
- 拼写检查和语法检查工作正常
- 自动拼写更正和文本替换不起作用(即使启用(
(已编辑 2018-05-30(
最新回复 (2018-05-22(:
这个问题又抬起了丑陋的头,我真的需要弄清楚。
-
我的自定义 NSTextStorage 与描述的基本相同,并且仍然有效。
-
我在NSTextView上使用自定义
insertText:replacementRange:
,但它调用[super insertText:replacementRange:]
以利用Apple的幕后工作,使拼写等工作更好。 我的自定义方法只需要设置一个布尔值。 -
在缩短文本时,我仍然收到Apple
insertText:replacementRange:
的请求,要求文本中不存在的部分的属性。 以前,我会卡在这里,因为我尝试的所有内容要么导致崩溃,要么导致 Apple 的代码无限期地重复请求不存在的属性。 -
最后,我尝试使用 NULL 范围指针返回假属性,这似乎让 Apple 的代码很高兴:
- (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]; } }
-
经过进一步的测试,结果证明这还不够。 我最终将以下内容添加到设置器中,以存储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]; } }
-
我更新了 'attributesAtIndex:effectiveRange: 以在使用无效范围调用时返回以下内容,而不是返回上面的假属性:
// Apparently returning fake attributes satisfies [NSTextView insertText] *range = _invalidRange; return _invalidAttrs;
这似乎在以前会触发异常或无限循环的各种条件下工作。