|
|
|||||||||
![]() |
|||||||||
|
![]() |
||||||||
|
DBPrefsWindowController Sunday, March 11, 2007 Application preference windows aren’t difficult to create. Just create an NSWindowController, set up a toolbar, create a nib file with either a series of custom views, or with an NSTabView object. The problem is, just setting up the toolbar and view swapping mechanism takes a lot of code, and it’s basically the same for every application. Sounds like an excellent opportunity to create a new class.
![]()
If you’re not already familiar with how to set up a preferences window, check out John Devor’s excellent Simple Preferences example. His example shows exactly how most preference windows are created in Cocoa applications.
The problem is, through no fault of John or anyone else that uses this technique, that it’s high maintenance code. If you take a look at his example’s PreferencesController.m file (an NSWindowController subclass), you’ll see that you need to add ten lines of code to five different methods, plus add a new static variable, to add one additional toolbar icon. If you want to move this code into another project, you’ll have to modify 50 lines of code to change the labels on the five toolbar icons (assuming you want the identifiers in the source code to still make sense).
My goal was to try to reduce this to one line of code for each toolbar icon. As with John’s example, I wanted each view to be an individual custom NSView in Interface Builder. I find this much easier to work with than trying to manage multiple tabs in an NSTabView.
It’s also important to have all of the preference views in their own nib file, so that they aren’t loaded into memory unless and until the user displays the preferences window.
So I’ve created an NSWindowController subclass named DBPrefsWindowController.
Download: DBPrefsWindowController 1.1.2[1]
Using DBPrefsWindowController
If you’re comfortable with subclassing and working with Interface Builder, you’ll be able to figure out how to use it from the included example project. If not, check back tomorrow when I post a full tutorial on how to use DBPrefsWindowController.
Basically you just subclass it and connect your .h file to a nib file of custom views. The nib file should be named “Preferences.” Then just call -addView:label: to specify which views to display and what labels to use:
- (void)setupToolbar { [self addView:generalPreferenceView label:@"General"]; [self addView:colorsPreferenceView label:@"Colors"]; [self addView:playbackPreferenceView label:@"Playback"]; [self addView:updatePreferenceView label:@"Update"]; [self addView:advancedPreferenceView label:@"Advanced"]; }
When setup this way, DBPrefsWindowController will look for image files with names that match the labels supplied here. If your application is localized, you can instead call -addView:label:image: to specify the images to use for the toolbar icons.
That’s really all there is to it. If you’ve already created a nib file with separate views for your preferences window, you can just use it instead of creating a new nib file. Either rename your nib file “Preferences,” or override the +nibName class method so it returns the name of your existing nib file.
Cross-fading Views
I’ve designed the DBPrefsWindowController class so that it follows both Apple’s HIG and the IndieHIG as closely as possible. However, there was one feature that I wanted that violates the IndieHIG. I wanted the window’s contents to fade between views. I’m pretty hard-core about sticking to Apple’s HIG, but I’ve seen this effect used in the preferences window of many other applications, so I don’t think I’m violating any norms here.
Here’s a short list of applications that I was able to find that cross-fade views in their preferences windows:
- Apple’s DVD Player
After doing a bit of research, I decided that using Cocoa’s NSViewAnimation class was the best approach.
The most difficult part was getting the fading views to stay put while the window is resizing. The combination of the view animation routines and the window resizing animation cause the fading views to scroll up and down as the window resizes. In fact, in all of the applications listed above, save for the one by Apple, you’ll notice the view move up or down as the window resizes. Actually FireFox avoids the problem by fading out a view, resizing the window, then fading in the next view.
After many more days of playing with this than I should have spent, I found a solution. So you’ll notice that the fading views stay put, just as you would expect, while the window resizes.
Documentation
The DBPrefsWindowController class is designed to be subclassed for each application in which it is used. The subclass should be set as the File’s Owner for a nib file named “Preferences.” The subclass should have NSView instances which are IBOutlets connected to views in the nib file.
The nib file does not need to be connected to a window. If it is, it will be ignored and a new window will be created to display the preference views.
+ (DBPrefsWindowController *)sharedPrefsWindowController This class method returns a shared instance of the DBPrefsWindowController class.
+ (NSString *)nibName Override this class if you want to use a nib file named something other than “Preferences.”
- (void)setupToolbar Override this method with calls to -addView:label: or -addView:label:image: to populate the toolbar.
- (void)addView:(NSView *)view label:(NSString *)label Call this method as many times as needed from -setupToolbar to add new toolbar icons and custom views to the preferences window. An image with a name that matches the label should be available in the application bundle. It will be used as the toolbar icon.
- (void)addView:(NSView *)view label:(NSString *)label image:(NSImage *)image This method can be used instead of -addView:label: if the application is localized, or if you just want to use icons with names that differ from the toolbar button labels.
- (IBAction)showWindow:(id)sender Call this method to display the preferences window. For example:
[[AppPrefsWindowController sharedPrefsWindowController] showWindow:nil];
- (void)setCrossFade:(BOOL)fade Call this method to enable or disable the cross-fade effect when switching views. The default value is YES.
- (void) setShiftSlowsAnimation:(BOOL)slow
Call this method to enable or disable the use of the shift key to slow down the animation when switching views. The default value is YES.
Footnotes: 1. After this was written, the class was updated. You can find out more about the changes here and here and here.
| |||||||||