user1137421
user1137421

Reputation:

Drawing into transparent overlay subview NSView

I've got a relatively simple Cocoa on Mac OS X 10.6 question to ask. I have a main NSView (ScreensaverView, actually) that is not layer backed and is otherwise unremarkable. It does some basic drawing in its drawRect via NSRectFill and NSBezierPath:stroke calls (dots and lines, basically).

I've also got a NSView-derived subclass that is acting as a child subview. I'm doing this with the goal to draw simple lines in the subview that draw on top of whatever is drawn in the main view, but then can be somehow "erased" revealing whatever the lines obscured. The code for this subview is quite simple:

- (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
    }
}

With the code as shown above, any time the subview draws, the main view goes black and you only see the subview line. When the subview isn't being updated, the main view contents appear again.

Things I've tried, with varying results:

I feel like I'm missing something really basic, but for whatever reason, I can't seem to figure it out. Any and all suggestions are greatly appreciated.

Upvotes: 1

Views: 2452

Answers (1)

Rob Keniger
Rob Keniger

Reputation: 46028

I think you are fundamentally misunderstanding how view drawing works. Basically, every time drawRect: is called on your view, the drawing commands in drawRect: are executed. This might happen automatically or when you issue the view a setNeedsDisplay: message.

Normally, when drawRect: is called on the view, the view is erased and drawing begins anew. Nothing remains in the view from the previous call to drawRect:. You do not need to fill the view with [NSColor clearColor] in order for it to be transparent.

Note that this is only the case if your view returns NO from isOpaque, which is the default. If your view is returning YES from isOpaque then you will need to ensure you erase the view before drawing, however in your particular case you should not return YES from isOpaque because you want your view to be transparent.

You do not need to "erase" the line you've drawn. It will be erased for you. Instead, you need to simply not draw it when you don't want it drawn.

Basically, your view should store some flag (such as your BOOL ivar named drawLine or something similar). Then, in your drawing code you should simply check if this value is set. If it is, you should draw the line. If not, then just do nothing. Your code should be reduced to this:

- (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
    }
}

If you want to change the state and redraw the view, all you need to do is change the value of the drawLine ivar and ask the view to redraw using [yourView setNeedsDisplay:YES].

You can easily do this with a timer:

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

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

Upvotes: 2

Related Questions