Edit AllPages

NSPreferences is a class the provides a pseudo-standard look-and-feel for editing application preferences. It is used in both Safari and Mail. It basically controls a tabbed view with a toolbar-style series of icons. IMHO, it is much more efficient for small sets of preferences than the PreferencesPane interface seen in System Preferences.

This falls under the UndocumentedGoodness category. There are a few minor hiccups, but after that, it’s smooth sailing.

Due to something funky in the NSPreferences init method, I’ve found that you have to do this override to avoid a segfault. I’m kind of interested in figuring out why this is, but for the time being, it was easier to hack around.

@interface MSPreferences : NSPreferences { }


@implementation MSPreferences


What you mostly need to do is extend the NSPreferencesModule and flesh it out with all the gory details of your interface.

@interface NSPreferencesModule:NSObject { NSBox *_preferencesView; // 4 = 0x4 struct _NSSize _minSize; // 8 = 0x8 char _hasChanges; // 16 = 0x10 void *_reserved; // 20 = 0x14 }


@interface SamplePreferencesModule : NSPreferencesModule { } @end

@implementation SamplePreferencesModule









You’ll need to define a nib file (and specify it’s name in the module class. You probably want to create an NSPreferencesModule class in InterfaceBuilder and make sure it has an outlet for NSBox* _preferencesView. Once you have that taken care of, you just need the following lines in you application:

[NSPreferences setDefaultPreferencesClass:[MSPreferences class]];
NSPreferences* prefs = [NSPreferences sharedPreferences];
[prefs addPreferenceNamed:@"SamplePreferencesModule" owner:[SamplePreferencesModule sharedInstance]];

Obviously, I’ve left out a few things from my actual implementation, but hopefully this can get you started. If you are having trouble, please drop me a note and I’ll try update these notes to help you solve your problem. –MikeSolomon

A more complete header for NSPreferences (and friends) that has 10.2 and 10.3 differences broken out.

#import <Foundation/NSObject.h> #import <Foundation/NSGeometry.h> #import <AppKit/NSNibDeclarations.h>

// Private classes from the AppKit framework; used by Safari and Mail.

@class NSWindow; @class NSMatrix; @class NSBox; @class NSButtonCell; @class NSImage; @class NSView; @class NSMutableArray; @class NSMutableDictionary;

@protocol NSPreferencesModule #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3

@interface NSPreferences : NSObject { #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_2 id preferencesPanel; NSBox *preferenceBox; id moduleMatrix; id okButton; id cancelButton; id applyButton; #else NSWindow *_preferencesPanel; NSBox *_preferenceBox; NSMatrix *_moduleMatrix; NSButtonCell *_okButton; NSButtonCell *_cancelButton; NSButtonCell *_applyButton; #endif NSMutableArray *_preferenceTitles; NSMutableArray *_preferenceModules; #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_2 NSMutableDictionary *_preferenceViews; #else NSMutableDictionary *_masterPreferenceViews; NSMutableDictionary *_currentSessionPreferenceViews; #endif NSBox *_originalContentView; BOOL _isModal; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3 float _constrainedWidth; id _currentModule; void *_reserved; #endif }


@interface NSPreferencesModule : NSObject { #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_2 IBOutlet NSBox *preferencesView; BOOL hasChanges; #else IBOutlet NSBox *_preferencesView; NSSize _minSize; BOOL _hasChanges; void *_reserved; #endif }

Also since _currentSessionPreferenceViews and _masterPreferenceViews were added in 10.3, leaving those allocs out in your custom override init method will allow your code to work in 10.2 and 10.3. NSPreferences seems to allocate these lazily. –TimothyHatcher

If you want to avoid stuff that isn’t officially allowed for use by Apple (it looks like NSPreferences is private), you can also use my UKPrefsPanel class, which does something similar when combined with NSUserDefaults. Not quite as advanced, but similar enough. It’s at – UliKusterer

Could someone post an example? I’m completely lost :(


I’m guessing there’s a reason why sending init doesn’t work. You probably have to call something like initWithWindow: or something like that… for debugging purposes someone probably threw in an assertation or something like it into the init method which is why you see your program exit.

I’m using this to add a preferences module to Safaris preferences, for my plug-in. The only problem I’ve encountered, is that I have to click “Preferences…” from the Safari menu TWO times, before it show up. Every time. Do anyone know why this happens? And any idea how to fix it…?

UPDATE: Safari saves the last preference module that was open. If my module was not the last, it has to be clicked on in the preferences toolbar twice. If it was the last, it is as described above. It seems like it has to be activated one time first to “activate” it, and then another time to really open.

UPDATE2: It is fixed! I had connected the preferencesView-outlet to a NSView, but it needed to be a NSBox. The naming confused me. (and maybe others, so I don’t delete this comment.)