Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cocoa/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 子类化NSScrollView drawRect:方法_Objective C_Cocoa_Xcode_Drawing_Nsview - Fatal编程技术网

Objective c 子类化NSScrollView drawRect:方法

Objective c 子类化NSScrollView drawRect:方法,objective-c,cocoa,xcode,drawing,nsview,Objective C,Cocoa,Xcode,Drawing,Nsview,我正在为我的一个应用程序定制UI,其想法是,文本区域在失焦时最初是灰色边框,当它进入焦点时,边框变成亮白色。我的应用程序使用黑色主题,对于单行的NSTextField,效果非常好 但是,我在使用子类NSTextView时遇到了问题。为了正确地修改边框,我最终不得不对父级NSScrollView进行子类化,但仍然看到奇怪的行为。(请参见下面的屏幕截图。)我希望红色框填充整个滚动视图,因为这将允许我笔划(而不是填充,这只是为了测试)路径,产生一个漂亮的边框。相反,红色框似乎只填充内部子视图 以下代码

我正在为我的一个应用程序定制UI,其想法是,文本区域在失焦时最初是灰色边框,当它进入焦点时,边框变成亮白色。我的应用程序使用黑色主题,对于单行的
NSTextField
,效果非常好

但是,我在使用子类
NSTextView
时遇到了问题。为了正确地修改边框,我最终不得不对父级
NSScrollView
进行子类化,但仍然看到奇怪的行为。(请参见下面的屏幕截图。)我希望红色框填充整个滚动视图,因为这将允许我笔划(而不是填充,这只是为了测试)路径,产生一个漂亮的边框。相反,红色框似乎只填充内部子视图

以下代码片段,用于
NSScrollView
子类:

- (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));
}
生成如下所示的屏幕截图。另外,请参阅日志中的输出,这表明应该填充整个视图。这是唯一显示的输出,无论内部文本大小如何。输入回车会增加红色框的高度,但不会产生不同的输出。(我希望红色的框填充整个边界。)

2011-04-08 21:30:29.789 MyApp[6515:903]我的界限:{{0,1},{196,87}
2011-04-08 21:30:29.789 MyApp[6515:903]Super()界限:{{0,1},{196,87}

编辑:感谢Josh Caswell的回答。请参阅下文,了解未聚焦和聚焦时的正确行为


正如ughoavgfhw所指出的,
NSScrollView
通常不进行任何绘图,并且可能以这种方式与其子视图进行奇怪的交互。我建议在文本视图的绘图代码中添加如下内容,以绘制所需的自定义聚焦环*:

// 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有点允许在视图的边界之外绘制——我不能保证这是完全安全的,但您似乎可以使用3px的余量。如果你愿意,你可以把戒指完全画在边界内。一个真正的聚焦环在视图内部略微延伸(2像素)(正如我在这里所做的)

苹果公司的文件

编辑:在重新阅读了你对这个问题的评论之后,我意识到我可能已经长篇大论地掩盖了真正的答案。尝试子类化
NSClipView
并切换滚动视图的剪辑视图,或者使用自定义视图作为文档视图



*:您还可以将其放入自定义视图子类的图形代码中,该子类被设置为
NSScrollView
的文档视图;然后,您的文本视图可以是该视图的子视图。或者替换自定义的
NSClipView
子类。

您希望边框始终完全可见,还是只在文本视图边缘可见的区域可见?好问题。我希望边框在滚动视图的边界周围始终可见。(不是文本视图的边界。)边界将始终可见,但如果不是第一个响应者,则为浅灰色,如果有焦点,则为亮白色。(背景是深色的。)这可能是剪辑的问题。使用
cgcontextgetclipboondingbox((CGContextRef)[[NSGraphicsContext currentContext]graphicsPort]查找当前剪辑矩形并发布它是什么。您可以通过不调用
[super drawRect://code>来解决此问题,这是可以的,因为NSScrollView通常不会进行任何绘图。完美-我实际上需要做的就是使用滚动视图的边界创建NSBezier*clipPath,然后调用setClip!谢谢@克雷格:我很高兴你能从中挑出一些有用的信息!通过使用NSRect outerRect=NSInsetRect(界限,-2,-2),可以再次将NSInsetRect用于outerRect
// 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];