oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Adding a New Style Preferences Window to Your App, Part 1
Pages: 1, 2, 3, 4

Running the Application

Build and run the NewPreferencesExample application in Xcode. If everything goes to plan, a window will appear in screen. Hit Command-Q to exit the application. There's nothing more to see here--please move along.

Adding a Preferences Window

The first step is to add the SS_PrefsController source files, SS_PrefsController.h, SS_PrefsController.m, and SS_PreferencePaneProtocol.h, to our XCode project. I prefer to put these in the Other Sources group.

Now open the SS_PreferencePaneProtocol.h file. It contains seven methods, with the following signatures:

+ (NSArray *)preferencePanes;
- (NSView *)paneView;
- (NSString *)paneName;
- (NSImage *)paneIcon;
- (NSString *)paneToolTip;
- (BOOL)allowsHorizontalResizing;
- (BOOL)allowsVerticalResizing;

SS_PrefsController requires each preference pane to implement the SS_PreferencePaneProtocol protocol. In the example that comes with SS_PrefsController, each preference pane implementation (controller) class implements this protocol anew, with the values for the pane name, icon, tooltip, and other properties hardcoded in the pane controller implementation.

Inspecting the source for the SS_PrefsController class, we see that it enforces the rule that preference pane controllers must implement the SS_PreferencePaneProtocol only in the designated initializer (- (id)initWithPanesSearchPath:(NSString*)path bundleExtension:(NSString *)ext).

Instead of implementing the SS_PreferencePaneProtocol protocol in our PreferencePaneControllers, we will provide a parent class that replicates the functionality of the SS_PreferencePaneProtocol, from which our PreferencePaneControllers will inherit.

The Preference Pane Controller

Here is the header for our NPEPreferencePaneController class:

#import <Cocoa/Cocoa.h>

#define PANE_NAME_KEY @"paneName"
#define ICON_PATH_KEY @"iconPath"
#define TOOL_TIP_KEY @"toolTip"
#define HELP_ANCHOR_KEY @"helpAnchor"

#define ALLOWS_HORIZONTAL_RESIZING_KEY @"allowsHorizontalResizing"
#define ALLOWS_VERTICAL_RESIZING_KEY @"allowsVerticalResizing"

@interface NPEPreferencePaneController : NSObject{
    id _controller;
    NSString *_nibName;
    NSString *_paneName;    
    NSString *_iconPath;
    NSString *_toolTip; 
    NSImage *_paneIcon;

    BOOL _allowsHorizontalResizing;
    BOOL _allowsVerticalResizing;

    NSString *_helpAnchor; 
    IBOutlet NSView *_prefsView;

- (id) initWithNib:(NSString *)nibName dictionary:(NSDictionary *)dictionary controller:(id)controller;

- (void) dealloc;

- (id) controller;

- (void) showWarningAlert:(NSError *) error;
- (IBAction) showHelp:(id) sender;

// SS_PreferencePaneProtocol

// we don't need this.
// + (NSArray *)preferencePanes;
- (NSView *)paneView;
- (NSString *)paneName;
- (NSImage *)paneIcon;
- (NSString *)paneToolTip;
- (BOOL)allowsHorizontalResizing;
- (BOOL)allowsVerticalResizing;


As you can see, we have class variables for each of the properties that a pane controller requires. We also have a pointer to a controller instance, and to an NSView object--the NSView that will contain the controls for our preference pane.

The methods for our NPEPreferencePaneController closely mimic those of the SS_PreferencePaneProtocol, with the following exceptions: we have eliminated the preferencePanes method, as we will not need it, and we have added showWarningAlert and showHelp methods. We will describe these later on.

We have also added an initializer that takes as its arguments the name of the nib for the preference pane, a dictionary (from which we will read the other parameters), and a pointer to a controller--in our case, this will be our NPEController instance.

Our implementation for NPEPreferencePaneController is shown below:

#import "NPEPreferencePaneController.h"

@implementation NPEPreferencePaneController

- (id) initWithNib:(NSString *)nibName dictionary:(NSDictionary *)dictionary controller:(id)controller{
    if(! (self = [super init])) return nil;

    _nibName = nibName;
    _paneName = [dictionary objectForKey:PANE_NAME_KEY];
    _iconPath = [dictionary objectForKey:ICON_PATH_KEY];
    _toolTip = [dictionary objectForKey:TOOL_TIP_KEY];

    NSAssert(_nibName && _paneName && _iconPath && _toolTip,
             @"Dictionary does not contain a nibName, paneName, iconPath, or toolTip entry.");

    [_nibName retain];
    [_paneName retain];
    [_iconPath retain];
    [_toolTip retain];

    _helpAnchor = [[dictionary objectForKey:HELP_ANCHOR_KEY] retain];
    _allowsHorizontalResizing = [@"YES" isEqualToString:[dictionary objectForKey:ALLOWS_HORIZONTAL_RESIZING_KEY]];
    _allowsVerticalResizing = [@"YES" isEqualToString:[dictionary objectForKey:ALLOWS_VERTICAL_RESIZING_KEY]];

    _controller = [controller retain];

    return self;

- (void) dealloc{
    [_nibName release];
    [_paneName release];
    [_iconPath release];
    [_toolTip release];
    [_paneIcon release];
    [_helpAnchor release];
    [_controller release];
    [super dealloc];

- (id) controller{ return _controller; }

- (void) showWarningAlert:(NSError *) error{
    NSAssert(_prefsView != nil, @"prefsView was nil");
    NSAlert *alert = [NSAlert alertWithError:error];
    if(_helpAnchor != nil){
        [alert setShowsHelp:YES];
        [alert setHelpAnchor:_helpAnchor];

    [alert setAlertStyle:NSWarningAlertStyle];
    [alert beginSheetModalForWindow:[_prefsView window]

- (IBAction) showHelp:(id) sender{
    NSAssert(_helpAnchor, @"Help anchor was not set");
    [[NSHelpManager sharedHelpManager] openHelpAnchor:_helpAnchor inBook:nil];

- (NSView *)paneView{
    BOOL loaded = YES;
    if(! _prefsView) loaded = [NSBundle loadNibNamed:_nibName owner:self];
    if(loaded) return _prefsView;
    return nil;

- (NSString *)paneName{ return _paneName; }

- (NSImage *)paneIcon{
    if(_paneIcon == nil){
        _paneIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] 
    return _paneIcon;

- (NSString *)paneToolTip{ return _toolTip; }

- (BOOL)allowsHorizontalResizing{ return _allowsHorizontalResizing; }

- (BOOL)allowsVerticalResizing{ return _allowsVerticalResizing; }


With the exception of the initializer, and the showHelp and showWarningAlert methods, this is lifted almost wholesale from the example SS_PreferencePaneProtocol implementations that come with the SS_PrefsController examples.

Pages: 1, 2, 3, 4

Next Pagearrow