objective-cmacoscocoansviewnsopenglview

NSView containing NSOpenGLView doesn't redraw after a subview is removed


I have a Mac OS X (10.9) application with a custom NSView for my main NSWindow's contentView property. The only thing the custom view does is override drawRect: so that it's transparent. Transparency is required so that my NSOpenGLView is visible (see below):

/* Transparent NSView Subclass */
- (void)drawRect:(NSRect)dirtyRect
{
  NSLog(@"Drawing Rect");
  [[NSColor clearColor] set];
  NSRectFillUsingOperation(dirtyRect, NSCompositeClear);
}

The contentView has an NSOpenGLView as a subview, with its surface order set to -1 (it's 'below' the main NSWindow, which is also transparent):

/* NSOpenGLView subclass: */
GLint order = -1;
[self.openGLContext setValues:&order forParameter:NSOpenGLCPSurfaceOrder];

I then instantiate a WebView and place it as a subview of this custom view:

_webview = [[WebView alloc] initWithFrame:frame];
[_webview setMaintainsBackForwardList:NO];
[_webview setTranslatesAutoresizingMaskIntoConstraints:NO];
[_webview setDrawsBackground:NO];
[_window.contentView addSubview:_webview];

The WebView is a small box in the window (on top of the NSOpenGLView) which is used to login to a service. Once login happens it should disappear, showing only the NSOpenGLView.

Here's the problem: when I call [_webview removeFromSuperview]; the contentView does not redraw; therefore, the WebView is still drawn on the screen (although not responsive to mouse or keyboard). If I use my mouse to resize the window that everything is in, the contentView redraws (I see the message in the logs) and the WebView's content disappears. Also, if I don't add the NSOpenGLView as a subview, the WebView goes away as it should.

Shouldn't the contentView fire a drawRect after a subview is removed? Even when I used setNeedsDisplay: or setNeedsLayout: after removing the WebView the contentView still didn't redraw.


Solution

  • Apple's advice these days is to use Core Animation layers to put normal views on top of OpenGL views.

    I suspect that the content view has redrawn itself. It draws clear. What you're seeing is the OpenGL surface behind it. The nearly-raw-VRAM nature of OpenGL surfaces may mean that the web view that was drawn on top of it actually modified the contents of the surface. So, I recommend that you force the OpenGL view to redraw itself. If it draws in its -drawRect:, then it should be enough to send it -setNeedsDisplay:. If you do rendering outside of -drawRect: by manually making the context current, issuing rendering commands, and then calling -flushBuffer, then you'll need to do that.