子类化NSScrollView drawRect:方法 [英] Subclassing NSScrollView drawRect: Method

查看:140
本文介绍了子类化NSScrollView drawRect:方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我自定义了我的应用程序的UI,并且想法是当没有焦点时,文本区域最初是灰色的,当焦点对准时,边框变成明亮的白色。我的应用程序使用一个黑暗的主题,对于单行的 NSTextField ,这是非常好的。

I'm customizing the UI for one of my apps, and the idea is that a text area is initially bordered gray when out of focus, and when it comes into focus, the border becomes bright white. My app uses a dark theme, and for a single-lined NSTextField, this works great.

然而,遇到子类 NSTextView 的问题。为了正确改变边界,我最终不得不实际子类 NSScrollView ,但仍然看到奇怪的行为。 (见下面的屏幕截图)希望红色框填充整个滚动视图,因为这将允许我敲击(而不是填充,这只是用于测试)路径,产生一个漂亮的边框。

I'm running into problems with a subclassed NSTextView, however. In order to alter the border properly, I ended up having to actually subclass the parent NSScrollView, but am still seeing strange behavior. (See screenshot below.) I want the red box to fill the entire scroll view, as this would allow me to stroke (instead of filling, which is just for testing) the path, producing a nice border. Instead, the red box seems to be only filling to the internal child view.

下面的代码片段,用于 NSScrollView subclass:

The following code snippet, which is for the NSScrollView subclass:

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    NSRect borderRect = self.bounds;
    borderRect.origin.y += 1;
    borderRect.size.width -= 1;
    borderRect.size.height -= 4;

    BOOL inFocus = ([[self window] firstResponder] == self);

    if (!inFocus) {
        inFocus = [self anySubviewHasFocus:self];
    }

    if (inFocus) {
        [[NSColor colorWithDeviceRed:.8 green:.2 blue:0 alpha:1] set];
    } else {
        [[NSColor colorWithDeviceRed:.1 green:.8 blue:0 alpha:1] set];
    }

    [NSGraphicsContext saveGraphicsState];
    [[NSGraphicsContext currentContext] setShouldAntialias:NO];
    [NSBezierPath fillRect:borderRect];
    [NSGraphicsContext restoreGraphicsState];

    NSLog(@"My bounds: %@", NSStringFromRect(borderRect));
    NSLog(@"Super (%@) bounds: %@", [self superview], NSStringFromRect(borderRect));
}

生成如下所示的屏幕截图。此外,请参阅日志中的输出,这表示应填充整个视图。 这是唯一显示的输出,不管内部文本的大小。输入回车将增加红色框的高度,但不会产生不同的输出。 (我希望红色框填充整个边界。)

Produces the screenshot as seen below. Also, see the output in the log, which suggests that the entire view should be filled. This is the only output that is ever shown, regardless of the size of the text inside. Entering carriage returns increases the height of the red box, but does not produce different output. (And I would like the red box to fill the entire bounds.)

2011-04-08 21:30:29.789 MyApp[6515:903] My bounds: {{0, 1}, {196, 87}}
2011-04-08 21:30:29.789 MyApp[6515:903] Super (<EditTaskView: 0x3a0b150>) bounds: {{0, 1}, {196, 87}}

编辑: / strong>感谢 Josh Caswell 的回答。

推荐答案

正如ughoavgfhw指出的, NSScrollView 并可能有一个奇怪的互动与其子视图的方式。我建议在您的文本视图的绘图代码中添加类似以下内容来绘制您想要的自定义聚焦环:

As ughoavgfhw noted, NSScrollView doesn't usually do any drawing, and probably has a weird interaction with its child views in that way. I'd suggest putting something like the following in your text view's drawing code to draw this custom focus ring that you want*:

// We're going to be modifying the state for this, 
// so allow it to be restored later
[NSGraphicsContext saveGraphicsState];

// Choose the correct color; isFirstResponder is a custom     
// ivar set in becomeFirstResponder and resignFirstResponder
if( isFirstResponder && [[self window] isKeyWindow]){
    [myFocusedColor set];
} 
else {
    [myNotFocusedColor set];
}

// Create two rects, one slightly outset from the bounds,
// one slightly inset
NSRect bounds = [self bounds];
NSRect innerRect = NSInsetRect(bounds, 2, 2);
NSRect outerRect = NSMakeRect(bounds.origin.x - 2, 
                              bounds.origin.y - 2,
                              bounds.size.width + 4,
                              bounds.size.height + 4);

// Create a bezier path using those two rects; this will
// become the clipping path of the context
NSBezierPath * clipPath = [NSBezierPath bezierPathWithRect:outerRect];
[clipPath appendBezierPath:[NSBezierPath bezierPathWithRect:innerRect]];

// Change the current clipping path of the context to 
// the enclosed area of clipPath; "enclosed" defined by 
// winding rule. Drawing will be restricted to this area.
// N.B. that the winding rule makes the order that the
// rects were added to the path important.
[clipPath setWindingRule:NSEvenOddWindingRule];
[clipPath setClip];
// Fill the rect; drawing is clipped and the inner rect
// is not drawn in
[[NSBezierPath bezierPathWithRect:outerRect] fill];
[NSGraphicsContext restoreGraphicsState];

这应该是AppKit绘制聚焦环时的合理近似值。当然,AppKit是允许绘制一个视图的边界外 - 我不能保证这是完全安全,但你似乎得到一个3像素的边缘玩。如果你想要的话,你可以将圆环完全绘制在边界内。

This should be a reasonable approximation of what AppKit does when it draws a focus ring. Of course, AppKit is sort of allowed to draw outside a view's bounds -- I can't guarantee that this is completely safe, but you seem to get a margin of 3 px to play with. You could draw the ring entirely inside the bounds if you wanted. A true focus ring extends slightly (2 px) inside the view anyways (as I've done here).

Apple文档

Apple docs on Setting the Clipping Region.

编辑:在重新阅读了你对这个问题的意见后,我意识到我可能已经长期掩埋了真正的答案。请尝试将 NSClipView 作为子类,然后切换滚动视图的剪辑视图,或使用文档视图的自定义视图。

After re-reading your comments on the question, I realize I may have long-winded-ly buried the real answer. Try either subclassing NSClipView and switching your scroll view's clip view for that, or using a custom view for the document view.

*:你也可以把它放在自定义视图子类的绘图代码中,它被设置为 NSScrollView ;那么你的文本视图可以是它的子视图。或替换自定义的 NSClipView 子类。

*: You could also put this in the drawing code of a custom view subclass which is set as the document view of the NSScrollView; then your text view could be a subview of that. Or substitute a custom NSClipView subclass.

这篇关于子类化NSScrollView drawRect:方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆