首页 文章

NSTextView在图层上没有显示红色拼写错误的下划线

提问于
浏览
6

NSTextView 是层支持的 NSView 的子视图( -wantsLayer == YES )时,它不会为拼写错误的单词呈现波浪形的红色下划线 . 重现这一点就是制作一个空的Cocoa项目,打开笔尖,将 NSTextView 拖到窗口中,然后切换窗口的内容视图以获得一个图层 . 繁荣 - 没有更多的红色下划线 .

我做了一些搜索,这似乎是一个已知的情况,自10.5以来一直如此 . 但是,我找不到的是它的解决方法 . 当 NSTextView 在图层支持的视图中时,是否无法获取下划线?

我可以想象覆盖 NSTextViewdrawRect: 并使用布局管理器找到适当的临时属性集,指示拼写错误,然后自己绘制红色曲线,但这当然是完全黑客攻击 . 我也可以想象苹果公司在10.7(或许)中修复此问题,突然我的应用程序会有双重下划线或其他东西 .


[更新]我的解决方法

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

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

我相信,我发现了两个截然不同的问题:

#1 是NSTextView,当托管在图层支持的视图中时,没有定位的东西等导致它们在我的情况下根本不出现 . 目前未知 . )

#2 是当NSTextView处于与图层相关的情况时,它似乎没有正确地将文本标记为拼写错误're typing it - even when -isContinuousSpellCheckingEnabled is set to YES. I verified this by implementing some of the spell checking delegate methods and watching as NSTextView sent messages about changes but never any notifying to set any text ranges as misspelled - even with obviously misspelled words that would show the red underline in TextEdit (and other text views in other apps). I also overrode NSTextView' s -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行为完全不同,但是它很接近并且它现在可以完成工作 . 希望这对其他人有帮助,或者更好的是,有人来到这里告诉我,我错过了一些非常简单的东西,并解释了如何解决它 . :)

2 回答

  • 3

    核心动画非常棒,除非涉及到文本 . 当我发现在处理图层支持的视图时(在技术上可以通过设置不透明的backgroundColor并确保绘制背景),我发现子像素抗锯齿不是给定的第一手资料 . 子像素消除锯齿只是处理文本和图层支持视图时遇到的许多警告之一 .

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

    如果没有覆盖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提交文件错误报告,希望有一天会成为过去!

  • 1

    这也是一个黑客,但我唯一可以做的就是在 NSTextView 的图层上放置一个中间委托,以便所有选择器都通过,但 drawLayer:inContext: 然后调用NSTextView的 drawRect: . 虽然我不确定它是否会破坏任何CALayer动画,但这可能会有一些未来的证据 . 你似乎也必须修复 CGContextRef 的CTM(基于背衬层框架?) .

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

    我不完全确定如何解决这个问题,因为调用 drawRect: 就像我做的那样有点hackish,但我确信网上的某个人有一个关于这样做的教程 . 如果我有时间并且有时间,我也许可以制作一个 .

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

相关问题