7

当 an是 layer-backed ( )NSTextView的子视图时,它不会为拼写错误的单词呈现波浪状的红色下划线。重现这一点只需创建一个空的 Cocoa 项目,打开 nib,拖入窗口,然后切换窗口的内容视图以显示图层。繁荣 - 不再有红色下划线。NSView-wantsLayer == YESNSTextView

我进行了一些搜索,这似乎是一种已知情况,并且自 10.5 以来一直如此。但是,我找不到的是一种解决方法。NSTextView当处于图层支持的视图中时,是否没有办法让下划线呈现?

我可以想象覆盖NSTextView'sdrawRect:并使用布局管理器找到具有指示拼写错误的适当临时属性集的适当矩形,然后自己绘制红色曲线,但这当然是一个彻底的黑客。我也可以想象 Apple 在 10.7(也许)中修复了这个问题,突然我的应用程序会有双下划线或其他东西。


[更新] 我的解决方法

我目前的解决方法是受到 nptacek 提到的拼写检查委托方法的启发,这促使我深入挖掘了一条我以前没有注意到的路径,所以我将接受这个答案,但发布我为后代和/或进一步所做的事情讨论。

我正在运行 10.6.5。我有一个 NSTextView 的子类,它是 NSClipView 的自定义子类的文档视图,它又是我的窗口的 contentView 的子视图,它打开了图层。在玩这个的过程中,我最终将所有自定义项都注释掉了,但拼写检查仍然无法正常工作。

我隔离了我认为是两个不同的问题:

#1是 NSTextView,当托管在图层支持的视图中时,甚至不会费心绘制拼写错误的下划线。(我根据谷歌搜索收集到,它可能在 10.5 天内画了下划线,但不是在正确的位置 - 所以苹果可能刚刚完全禁用它们以避免在 10.6 中出现这个问题。我不确定.我如何定位东西等也可能有一些副作用,导致它们根本不会出现在我的案例中。目前未知。)

#2是当 NSTextView 处于这种与图层相关的情况时,它似乎在您键入文本时没有正确地将文本标记为拼写错误 - 即使 -isContinuousSpellCheckingEnabled 设置为 YES 也是如此。我通过实现一些拼写检查委托方法来验证这一点,并观察 NSTextView 发送有关更改的消息,但从未通知将任何文本范围设置为拼写错误 - 即使明显拼写错误的单词会在 TextEdit(和其他文本视图中显示红色下划线)在其他应用程序中)。我还覆盖了 NSTextView 的 -handleTextCheckingResults:forRange:types:options:orthography:wordCount: 以查看它所看到的内容,并且在那里看到了同样的内容。就好像 NSTextView 正在积极地将光标下的单词设置为没有拼写错误,然后当用户键入空格或离开它或其他什么时,它没有 t 重新检查拼写错误。不过,我并不完全确定。

好的,为了解决#1问题,我在我的自定义 NSTextView 子类中覆盖了 -drawRect: ,看起来像这样:

- (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 行为不太一样,但它有点接近,现在可以完成工作。希望这对其他人有所帮助,或者更好的是,有人来这里告诉我我遗漏了一些非常简单的东西并解释了如何解决它。:)

4

2 回答 2

4

Core Animation 很棒,除了文本。当我发现在使用支持图层的视图时(从技术上讲,您可以通过设置不透明的背景颜色并确保绘制背景来解决此问题),并没有给定子像素抗锯齿,我亲身体验了这一点。亚像素抗锯齿只是处理文本和支持图层的视图时遇到的许多警告之一。

在这种情况下,您有几个选择。如果可能,请远离使用文本视图的程序部分的图层支持视图。如果您已经尝试过,并且无法避免,那么还有希望!

无需覆盖drawRect,您可以使用以下代码实现接近标准行为的东西:


- (NSArray *)textView:(NSTextView *)view didCheckTextInRange:(NSRange)range types:(NSTextCheckingTypes)checkingTypes options:(NSDictionary *)options results:(NSArray *)results orthography:(NSOrthography *)orthography wordCount:(NSInteger)wordCount
{
     for(NSTextCheckingResult *myResult in results){
        if(myResult.resultType == NSTextCheckingTypeSpelling){
            NSMutableDictionary *attr = [[NSMutableDictionary alloc] init];
            [attr setObject:[NSColor redColor] forKey:NSUnderlineColorAttributeName];
            [attr setObject:[NSNumber numberWithInt:(NSUnderlinePatternDot | NSUnderlineStyleThick | NSUnderlineByWordMask)] forKey:NSUnderlineStyleAttributeName];
            [[inTextView layoutManager] setTemporaryAttributes:attr forCharacterRange:myResult.range];
            [attr release];
        }
    }
    return results;
}
我们基本上是在为 NSTextView 做一个快速而简单的委托方法(确保在 IB 中设置委托!),它检查一个单词是否被标记为不正确,如果是,则设置一个彩色下划线。

请注意,此代码存在一些问题 - 即带有下划线的字符(例如 g、j、p、q、y)将无法正确显示下划线,并且仅对拼写错误进行了测试(没有语法检查这里!)。下划线点模式 (NSUnderlinePatternDot) 与 Apple 的拼写检查样式不匹配,即使视图禁用了图层支持,代码仍处于启用状态。此外,我确信还有其他问题,因为这段代码又快又脏,而且还没有检查内存管理或其他任何东西。

祝您工作顺利,向 Apple 提交错误报告,希望这有一天会成为过去!

于 2010-12-07T04:45:30.973 回答
1

这也有点小技巧,但我唯一能做的就是在NSTextView's 层上放置一个中间委托,以便所有选择器都通过,drawLayer:inContext:然后调用 NSTextView 的drawRect:. 这行得通,并且可能对未来有更多的证明,尽管我不确定它是否会破坏任何 CALayer 动画。看来您还必须修复CGContextRef的 CTM(基于支持层框架?)。

编辑:drawInContext:您可以像文档中 那样使用 获取绘图矩形CGContextGetClipBoundingBox(ctx),但 . 中的翻转坐标可能存在问题NSTextView

我不完全确定如何解决这个问题,drawRect:因为我所做的调用有点骇人听闻,但我确信网上有人有关于这样做的教程。如果/当我有时间并解决它时,也许我可以制作一个。

寻找 . 的NSCell支持可能是值得的NSTextView,因为使用它可能更合适。

于 2010-12-06T22:28:47.287 回答