【问题标题】:Drawing into transparent overlay subview NSView绘制到透明覆盖子视图 NSView
【发布时间】:2012-02-05 12:15:57
【问题描述】:

我有一个相对简单的 Cocoa on Mac OS X 10.6 问题要问。我有一个主要的NSView(实际上是ScreensaverView),它没有层支持,并且在其他方​​面不起眼。它通过NSRectFillNSBezierPath: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] 来制作两个(和/或只是子视图)图层支持的视图,这会导致完全黑屏。

我觉得我错过了一些非常基本的东西,但无论出于何种原因,我似乎无法弄清楚。非常感谢任何和所有建议。

【问题讨论】:

    标签: macos cocoa


    【解决方案1】:

    我认为您从根本上误解了视图绘图的工作原理。基本上,每次在视图上调用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
        }
    }
    

    如果你想改变状态并重绘视图,你需要做的就是改变drawLine 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 ,整体显示只会永远显示绘制的线条。我假设我需要在子视图上 setNeedsDisplay:YES (但将 drawLine 设置为关闭)如果以及何时不再显示线条?
    • 完全正确。处理此问题的正常方法是使用NSTimer 在一段时间后触发重绘或持续动画更新可能是CVDisplayLink 回调。
    • 我添加了一个如何使用NSTimer 更新视图的示例。
    猜你喜欢
    • 2015-06-16
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    • 2017-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多