As part of my current Cocoa project I have a window hierarchy like:
NSWindow
|
+---Content View
|
+---NSSplitView
| |
| +---Custom NSView
| +---Custom NSView
| ...
|
+---Custom NSView
When populating all of those custom views I need to fetch some data from the network. This can take quite a bit of time and I’d like to give the user some visual cue that I’m not just sitting around drinking beer, but am vigilently trying to get their data.
I settled on graying out the entire window’s contents and using a label and progress bar to clue the user in on how things were moving along. The first thing I did was whip up an NSView with a black background and an alpha of 0.5 to get the translucent grayed out effect. I then added some custom drawn text in the view’s drawRect: method and a BGHUDProgressIndicator (because it looks sexy on the grayed out layer and I’ve never been a fan of the blue in the regular NSProgressBar). It looked great. Really.
Then I realized, where does this go?
Sibling NSViews that overlap one another have undefined behavior in Cocoa, so it’s not as simple as overlapping the NSWindow’s entire content view (I tried this and it kind of worked, but the progress bar didn’t update as it should have). I had a few ideas of how I might get around this, but decided I’d solicit some advice from people much smarter than me. So I asked StackOverflow.com. As it turns out Peter Hosey knew exactly what was up and suggested adding a child window. So I went to read up on NSWindow’s addChildWindow:ordered: method.
It turns out this solution works very well. I subclassed NSWindow to generate a window that could become key. It also observes notifications of the parent window moving or resizing and updates its frame accordingly. This means adding the child window consists of exactly one line in my NSWindowController subclass:
overlayWindow = [[SZOverlayWindow alloc] initWithParentWindow:self.window
withView:subview];
I decided to keep the SZOverlayWindow class very sparse so as to make it reusable as possible. I did all of the heavy lifting in the subview passed to it. That’s where the custom text is drawn and the progress bar is updated.
I’m very happy with this solution and think it would also lend itself to other situations. For instance, a crosshair that is coherent across multiple subviews of an NSSplitView. Without this technique each subview would have to be able to draw its small piece of the crosshair.
The code is available. There is still a bit too much knowledge in the window (mainly regarding the translucent nature), but it’s a good start for anyone tackling a similar problem.