Macos 绘制到透明覆盖子视图NSView中

Macos 绘制到透明覆盖子视图NSView中,macos,cocoa,Macos,Cocoa,我有一个关于MacOSX10.6的相对简单的问题要问。我有一个主视图NSView(ScreensaverView,实际上),它不是以图层为背景的,在其他方面并不显著。它通过NSRectFill和NSBezierPath:stroke调用(基本上是点和线),在drawRect中进行一些基本绘图 我还有一个NSView派生的子类,它充当子视图。我这样做的目的是在子视图中绘制简单的线,这些线绘制在主视图中绘制的任何东西的顶部,但随后可以以某种方式“擦除”显示隐藏的任何线。此子视图的代码非常简单: -

我有一个关于MacOSX10.6的相对简单的问题要问。我有一个主视图
NSView
ScreensaverView
,实际上),它不是以图层为背景的,在其他方面并不显著。它通过
NSRectFill
NSBezierPath:stroke
调用(基本上是点和线),在
drawRect
中进行一些基本绘图

我还有一个
NSView
派生的子类,它充当子视图。我这样做的目的是在子视图中绘制简单的线,这些线绘制在主视图中绘制的任何东西的顶部,但随后可以以某种方式“擦除”显示隐藏的任何线。此子视图的代码非常简单:

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code here.
    }
    return self;
}

- (void)dealloc
{   
    [super dealloc];
}

- (void)drawRect:(NSRect)dirtyRect {

    // Transparent background
    [[NSColor clearColor] set];
    NSRectFillUsingOperation(dirtyRect, NSCompositeCopy);

    // If needed for this update, draw line
    if (drawLine) {
        // OMITTED: Code that sets opaque NSColor and draws a line using NSBezierPath:stroke
    }

    // If needed for this update, "erase" line
    if (eraseLine) {
        [[NSColor blackColor] set]; // clearColor?
        // OMITTED: Code that draws the same line using NSBezierPath:stroke
    }
}
使用如上所示的代码,无论何时绘制子视图,主视图都会变为黑色,并且您只能看到子视图线。子视图未更新时,主视图内容将再次显示

我尝试过的事情,结果各不相同:

  • 我试着让子视图从被覆盖的
    isOpaque
    返回
    YES
    (我意识到这并不正确)。当我这样做时,两个视图在子视图更新期间都正确地绘制,但是子视图线覆盖了它所绘制的任何内容(确定),然后在删除时,也会在原来的位置留下一条大黑线(我试图避免的)。尝试使用
    clearColor
    而不是
    blackColor
    来“擦除”该行将导致该行保留在屏幕上

  • 我试图通过在
    init
    中调用
    [self-setWantsLayer:YES]
    来创建两个(和/或只是子视图)层备份视图,这导致了一个完全黑屏


我觉得我错过了一些非常基本的东西,但不管出于什么原因,我似乎无法理解。非常感谢您的任何建议。

我认为您根本误解了视图绘图的工作原理。基本上,每次在视图上调用
drawRect:
,都会执行
drawRect:
中的绘图命令。这可能会自动发生,或者在您发出查看
setNeedsDisplay:
消息时发生

通常,在视图上调用
drawRect:
时,视图将被删除,绘图将重新开始。上次调用
drawRect:
后,视图中没有任何内容保留。为了使视图透明,无需使用
[NSColor clearColor]
填充视图

请注意,只有当视图从
isOpaque
返回
NO
时才会出现这种情况,这是默认情况。如果视图从
isOpaque
返回
YES
,则需要确保在绘制之前删除视图,但是在特定情况下,不应从
isOpaque
返回
YES
,因为您希望视图透明

您不需要“擦除”已绘制的线。它将为你擦除。相反,当你不想画的时候,你只需要不画它

基本上,您的视图应该存储一些标志(例如您的
BOOL
ivar名为
drawLine
或类似的东西)。然后,在图形代码中,只需检查是否设置了此值。如果是,你应该划清界限。如果没有,那就什么也不做。您的代码应简化为:

- (void)drawRect:(NSRect)rect
{
    // If needed for this update, draw line
    if (drawLine) {
        // OMITTED: Code that sets opaque NSColor and draws a line using NSBezierPath:stroke
    }
}
如果要更改状态并重新绘制视图,只需更改
绘图线的值
ivar,并使用
[yourView setNeedsDisplay:YES]
要求视图重新绘制即可

您可以使用计时器轻松完成此操作:

- (void)awakeFromNib
{
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(update:) userInfo:nil repeats:YES];
}

- (void)update:(NSTimer*)timer
{
    drawLine = !drawLine;
    [self setNeedsDisplay:YES];
}

谢谢Rob,我感谢您的快速回复!但有一个问题——如果我像您描述的那样减少子视图drawRect,那么整个显示只会永远显示绘制的线条。我假设我需要在子视图上设置needs display:YES(但将drawLine设置为off),如果线条不再显示,以及何时显示?完全正确。处理此问题的正常方法是使用
NSTimer
在一段时间后重新绘制,或者使用
CVDisplayLink
回调进行持续的动画更新。我添加了一个如何使用
NSTimer
更新视图的示例。