Когда NSTextView
является подвидом NSView
, поддерживаемым слоями ( -wantsLayer == YES
), он не отображает волнистые красные подчеркивания для слов с ошибками. Все, что нужно для воспроизведения этого, - это создать пустой проект Какао, открыть перо, перетащить NSTextView
в окно и переключить представление содержимого окна, чтобы получить слой. Стрела - больше нет красных подчеркиваний.
Я немного поискал, и, похоже, это известная ситуация, которая сохраняется с 10.5. Однако я не могу найти обходного пути. Нет ли способа получить подчеркивание для рендеринга, когда NSTextView
находится в представлении с поддержкой слоев?
Я могу представить себе переопределение NSTextView
drawRect:
и используя диспетчер компоновки, чтобы найти нужные прямоугольники с правильным набором временных атрибутов, которые указывают на орфографические ошибки, а затем сам рисовал красные волнистые линии, но это, конечно, полный взлом. Я также могу представить, как Apple исправит это в 10.7 (возможно), и вдруг в моем приложении появятся двойные подчеркивания или что-то в этом роде.
Мое текущее обходное решение было основано на упомянутом nptacek методе делегата проверки орфографии, который побудил меня глубже изучить путь, который я не замечал раньше, поэтому я собираюсь принять этот ответ, но опубликую что я сделал для потомков и / или для дальнейшего обсуждения.
Я использую 10.6.5. У меня есть подкласс NSTextView, который является представлением документа пользовательского подкласса NSClipView, который, в свою очередь, является подвидом моего окна contentView, в котором включены слои. Играя с этим, я в конце концов закомментировал все настройки, но проверка орфографии работала некорректно.
Я выделил, как мне кажется, две отдельные проблемы:
# 1 то, что NSTextView, когда размещенный в режиме просмотра с поддержкой слоев, даже не пытается нарисовать подчеркивание с орфографической ошибкой. Я проверил это, реализовав некоторые методы делегата проверки орфографии и наблюдая, как NSTextView отправляет сообщения об изменениях, но никогда не уведомляет о том, чтобы установить какие-либо текстовые диапазоны как неправильно написанные - даже с явно неправильно написанными словами, которые отображали бы красное подчеркивание в TextEdit (и других текстовых представлениях). в других приложениях). Я также переопределил NSTextView -handleTextCheckingResults: forRange: types: options: orthography: wordCount:, чтобы увидеть, что он видел, и он увидел там то же самое. Это выглядело так, как если бы NSTextView активно устанавливал слово под курсором как исправное, а затем, когда пользователь вводил пробел или удалялся от него, или что-то еще, он не проверял слова на наличие ошибок. Однако я не совсем уверен.
Хорошо, поэтому, чтобы обойти # 1 , я переопределил -drawRect: в моем собственном подклассе NSTextView, чтобы он выглядел так:
- (void)drawRect:(NSRect)rect
{
[super drawRect:rect];
[self drawFakeSpellingUnderlinesInRect:rect];
}
Затем я реализовал -drawFakeSpellingUnderlinesInRect: чтобы использовать layoutManager для получения текстовых диапазонов, содержащих NSSpellingStateAttributeName в качестве временного атрибута, и визуализации точечного рисунка, достаточно близкого к стандартному точечному шаблону с ошибками OSX.
- (void)drawFakeSpellingUnderlinesInRect:(NSRect)rect
{
CGFloat lineDash[2] = {0.75, 3.25};
NSBezierPath *underlinePath = [NSBezierPath bezierPath];
[underlinePath setLineDash:lineDash count:2 phase:0];
[underlinePath setLineWidth:2];
[underlinePath setLineCapStyle:NSRoundLineCapStyle];
NSLayoutManager *layout = [self layoutManager];
NSRange checkRange = NSMakeRange(0,[[self string] length]);
while (checkRange.length > 0) {
NSRange effectiveRange = NSMakeRange(checkRange.location,0);
id spellingValue = [layout temporaryAttribute:NSSpellingStateAttributeName atCharacterIndex:checkRange.location longestEffectiveRange:&effectiveRange inRange:checkRange];
if (spellingValue) {
const NSInteger spellingFlag = [spellingValue intValue];
if ((spellingFlag & NSSpellingStateSpellingFlag) == NSSpellingStateSpellingFlag) {
NSUInteger count = 0;
const NSRectArray rects = [layout rectArrayForCharacterRange:effectiveRange withinSelectedCharacterRange:NSMakeRange(NSNotFound,0) inTextContainer:[self textContainer] rectCount:&count];
for (NSUInteger i=0; i<count; i++) {
if (NSIntersectsRect(rects[i], rect)) {
[underlinePath moveToPoint:NSMakePoint(rects[i].origin.x, rects[i].origin.y+rects[i].size.height-1.5)];
[underlinePath relativeLineToPoint:NSMakePoint(rects[i].size.width,0)];
}
}
}
}
checkRange.location = NSMaxRange(effectiveRange);
checkRange.length = [[self string] length] - checkRange.location;
}
[[NSColor redColor] setStroke];
[underlinePath stroke];
}
После этого я сделал это может видеть красные подчеркивания, но, похоже, он не обновляет состояние орфографии по мере того, как я печатаю. Чтобы обойти эту проблему, я реализовал следующие злые уловки в моем подклассе NSTextView:
- (void)setNeedsFakeSpellCheck
{
if ([self isContinuousSpellCheckingEnabled]) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(forcedSpellCheck) object:nil];
[self performSelector:@selector(forcedSpellCheck) withObject:nil afterDelay:0.5];
}
}
- (void)didChangeText
{
[super didChangeText];
[self setNeedsFakeSpellCheck];
}
- (void)updateInsertionPointStateAndRestartTimer:(BOOL)flag
{
[super updateInsertionPointStateAndRestartTimer:flag];
[self setNeedsFakeSpellCheck];
}
- (void)forcedSpellCheck
{
[self checkTextInRange:NSMakeRange(0,[[self string] length]) types:[self enabledTextCheckingTypes] options:nil];
}
Это не работает так же, как реальное, ожидаемое поведение OSX, но оно вроде близко и выполняет свою работу на данный момент. Надеюсь, это будет полезно для кого-то еще или, что еще лучше, кто-то придет сюда и скажет мне, что мне не хватает чего-то невероятно простого, и объяснит, как это исправить. :)