NSOpenGLView and Interface Builder
Okay, so I wanted to play some more with OpenGL in a Cocoa app. I opened my window's NIB, created an NSOpenGLView, and then changed it to have a "Custom class" setting pointing to a little subclass I'd created using Xcode's "Cocoa NSView subclass" template.
Okay, I thought, check out the headers: Oh yeah, it doesn't have an -initWithFrame: constructor, it actually has -initWithFrame:pixelFormat:. So, I changed my constructor accordingly.
So I step through the code with the debugger and notice that my instance variable with the color I'm using to draw never gets set up. Huh? A few more strategic breakpoints, and I see my constructor never gets called, but the -drawRect: method does. WTF??? So, I google a bit, and find a mailing list message about the un-kosher goings-on when IB instantiates an NSOpenGLView, and take their advice and just do my constructor work in -awakeFromNib for this one case.
I chide myself that I should really have looked at the docs first, go to Apple's web site, and find a QA document about the available Mac OS X OpenGL APIs. There is a link to the Cocoa OpenGL sample. Oddly, it claims to be Carbon for OS 9 and X, which doesn't make sense because it says it's Cocoa, and there's no OS 9 Cocoa. I should have taken that as a warning.
Instead, I download the sample, and look at the BasicOpenGLView.m source file. Whoo boy! That's more complicated than when I did all my OpenGL drawing by hand in OS 9! Having been burned before by Apple's BasicNSBrowser example (which made me not use NSBrowser because it just seemed too complicated to use, especially compared to NSOutlineView, when it really works just the same if you want it to), I realize that, perhaps, I should google for "minimal NSOpenGLView sample", and find A mention that what I have is it. Ouch. I bite the bullet and just dig through that code, trying to pick out the cherries that could be relevant to my situation. I add a the -prepareOpenGL method and a few other things.
Hellooo? Drawings? ... nobody home.
Something in the back of my head tells me that I've written so much junk, certainly I must have used NSOpenGLView before... I spotlight my hard disk for NSOpenGLView and find ... huh? MyOpenGLView in Apple's sample code? In /Developer/Examples/OpenGL/Cocoa/CocoaGL/. No underscore ... hmmm... I look at the source code, and it's the three lines of sample code I was looking for. I knew it was ridiculously simple.
Drawings? Hey! Where are you???
Maybe I overlooked something? I peek at that sample some more, and see a comment mentioning that this view mustn't be an OpenGLView dragged from IB. It must be a CustomView whose custom class has been set to your NSOpenGLView subclass. Well, ... okay ... ? I change my view's constructor to be -initWithFrame: again and call through to -initWithFrame:pixelFormat: after creating a suitable pixel format, like the example does.
So, yeah, it's a bit underwhelming, but I might as well have made this post part of my "considered harmful" series, because the NSOpenGLView object in the Interface Builder palette was apparently the only thing that kept my original code from working. After this, restoring all of my original code (except for the -initWithFrame:) didn't cause the drawings to disappear again.
I hear a bug report coming towards Apple...
Uli, did you try overriding -initWithCoder:? That's the init method that I expect to be called for all objects in a nib, excepting (1) 'Custom View' objects dragged from the palette, which receive initWithFrame:, and (2) custom subclasses of NSObject, which receive init.
This used to be explained in a document called "What Happens When a Nib File is Loaded", which was unfortunately replaced with something less accurate. I filed a bug for this.
Nevertheless, that doesn't sound like enough to completely remove your drawing, so something else is probably wrong too.
I agree with Ken. I inadvertantly stumbled across the same thing a while back looking at the OpenGL examples. I couldn't figure it out until I recalled that Aaron Hillegass has an OpenGL example in his "Cocoa Programming for Mac OS X" book. After stepping through that, it became obvious that initWithCoder was being called instead of initWithFrame:pixelFormat:. But even that took a while to figure out since BOTH methods are implemented in his example.
..as they should be. All designated initializers should always be overridden if a subclass needs any initialization of its own. (-copyWithZone: counts too, for classes that conform to NSCopying)
|Jim Hillhouse writes:|
Thank you for clarifying that NSOpenGLView is indeed NOT the view that uses, rather that a CustomView is. Yeah, big-time bug report. When one opens the LIbrary pallet in IB and sees under Views & Cells that ol' OpenGLView, it sure is easy to think that this is the one to use when drawing OpenGL in a window, even knowing that you must use your subclass of NSOpenGLView. So why does OpenGLView exist?
|Ross Franklin writes:|
Thank you so much. I was having the exact same problem, it was bugging me to no end. I had copy-pasted the code from the CocoaGL sample project, so I knew that it should be working, but I couldn't for the life of me figure out what was special in the sample project.
I now got it working, thanks to you. I also found another Apple tutorial that walks through setting up both views. This could still be made a bit more clear the by CocoaGL sample project.