Cocoa Programming for Mac OS X 第十三章(User Defaults)摘录

Many applications have Preferences panels that allow the user to choose a preferred appearance or behavior. The user's choices go into the user defaults database in the user's home directory. Note that only the choices that vary from the factory defaults are saved in the user defaults database. If you go to ~/Library/Preferences, you can see your user defaults database. The files are in property list format; you can browse through them with Property List Editor.

The NSUserDefaults class allows your application to register the factory defaults, save the user's preferences, and read previously saved user preferences.

NSUserDefaults

Every application comes with a set of defaults from the factory. When a user edits his or her defaults, only the differences between the user's wishes and the factory defaults are stored in the user's defaults database. Thus, every time the application starts up, you need to remind it of the factory defaults. This operation is called registering defaults.

After registering, you will use the user defaults object to determine how the user wants the app to behave. This process is called reading and using the defaults. The data from the user's defaults database will be read automatically from the filesystem.

You will also create a Preferences panel that will allow the user to set the defaults. The changes to the defaults object will be written automatically to the filesystem. This process is known as setting the defaults (Figure 13.3).

Figure 13.3. NSUserDefaults and the Filesystem


Here are some commonly used methods that are implemented in NSUserDefaults:

+ (NSUserDefaults *)standardUserDefaults

Returns the shared defaults object.


- (void)registerDefaults:(NSDictionary *)dictionary

Registers the factory defaults for the application.


- (void)setBool:(BOOL)value   forKey:(NSString *)defaultName
- (void)setFloat:(float)value forKey:(NSString *)defaultName
- (void)setInteger:(int)value forKey:(NSString *)defaultName
- (void)setObject:(id)value   forKey:(NSString *)defaultName

Methods for changing and saving a user's wishes.


- (BOOL)boolForKey:(NSString *)defaultName
- (float)floatForKey:(NSString *)defaultName
- (int)integerForKey:(NSString *)defaultName
- (id)objectForKey:(NSString *)defaultName

Methods for reading the defaults. If the user hasn't changed them, the factory defaults are returned.


- (void)removeObjectForKey:(NSString *)defaultName

Removes the user's preference, so that the application will return to using the factory defaults.

Precedence of Different Types of Defaults

So far, we have talked about two levels of precedence: What the user writes to his or her defaults database overrides the factory defaults. In fact, several more levels of precedence exist. These levels of default settings are known as domains. Here are the domains used by an application, from highest to lowest priority:

  • Arguments: Passed on the command line. Most people start their applications by double-clicking on an icon instead of by working from the command line, so this feature is seldom used in a production app.

  • Application: What comes from the user's defaults database.

  • Global: What the user has set for his or her entire system.

  • Language: What is set based on the user's preferred language.

  • Registered defaults: The factory defaults for the app.



Creating Keys for the Names of the Defaults

You will be registering, reading, and setting defaults in several classes in your application. To make sure that you always use the same name, you should declare those strings in a single file and then simply #import that file into any file in which you use the names.

There are several ways to do this. For example, you could use the C preprocessor's #define command, but most Cocoa programmers use global variables for this purpose. Add the following lines to your PreferenceController.h file after the#import statement:

extern NSString * const BNRTableBgColorKey;
extern NSString * const BNREmptyDocKey;

Now define these variables in PreferenceController.m. Put them after the #import lines but before @implementation :

NSString * const BNRTableBgColorKey = @"TableBackgroundColor";
NSString * const BNREmptyDocKey = @"EmptyDocumentFlag";

Why would you declare global variables that simply contain a constant string? After all, you could simply remember what the string was and type it in whenever you need it. The problem is that you might misspell the string. If the string is surrounded by quotes, the compiler will accept the misspelled string. In contrast, if you misspell the name of a global variable, the compiler will catch your error.

To keep the global variables from conflicting with another company's global variables, you have prefixed them with BNR (for Big Nerd Ranch). Global variables from Cocoa are prefixed with NS. These prefixes are important only when you start using classes and frameworks developed by third parties. (Note that class names are also global. You might prefer to prefix all your class names with BNR to keep them from conflicting with anyone else's classes.)


Registering Defaults

Each class is sent the message initialize before any other message. To ensure that your defaults are registered early, you will override initialize in AppController.m:

+ (void)initialize
{
    // Create a dictionary
    NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
    // Archive the color object
    NSData *colorAsData = [NSKeyedArchiver archivedDataWithRootObject:
                                               [NSColor yellowColor]];
    // Put defaults in the dictionary
    [defaultValues setObject:colorAsData forKey:BNRTableBgColorKey];
    [defaultValues setObject:[NSNumber numberWithBool:YES]
                      forKey:BNREmptyDocKey];
 
  
    // Register the dictionary of defaults
    [[NSUserDefaults standardUserDefaults]        
                                registerDefaults: defaultValues];
    NSLog(@"registered defaults: %@", defaultValues);
}

Because this is a class method, its declaration is prefixed with +.

Note that we had to store the color as a data object. NSColor objects do not know how to write themselves out as property list, so we pack them into a data object that does. The property list classes—NSStringNSArrayNSDictionary,NSCalendarDateNSData, and NSNumber—do know how to write themselves out as property lists. A property list comprises any combination of these classes. For example, a dictionary containing arrays of dates is a property list.


Letting the User Edit the Defaults

Next, you will alter the PreferenceController class so that the Preferences panel will cause the defaults database to get updated. Declare the following methods in PreferenceController.h:

- (NSColor *)tableBgColor;
- (BOOL)emptyDoc;

Make your PreferenceController.m file look like this:

#import "PreferenceController.h"
NSString * const BNRTableBgColorKey = @"TableBackgroundColor";
NSString * const BNREmptyDocKey = @"EmptyDocumentFlag";
 
  
@implementation PreferenceController
 
  
- (id)init{
if (![super initWithWindowNibName:@"Preferences"])
   return nil;
return self;
}

- (NSColor *)tableBgColor
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *colorAsData = [defaults objectForKey:BNRTableBgColorKey];
    return [NSKeyedUnarchiver unarchiveObjectWithData:colorAsData];
}

- (BOOL)emptyDoc
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    return [defaults boolForKey:BNREmptyDocKey];
}

- (void)windowDidLoad
{
    [colorWell setColor:[self tableBgColor]];
    [checkbox setState:[self emptyDoc]];
}

- (IBAction)changeBackgroundColor:(id)sender
{
    NSColor *color = [colorWell color];
    NSData *colorAsData =
                   [NSKeyedArchiver archivedDataWithRootObject:color];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:colorAsData forKey:BNRTableBgColorKey];
}

- (IBAction)changeNewEmptyDoc:(id)sender{
    int state = [checkbox state];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:state forKey:BNREmptyDocKey];
}

@end

					  
 
 

In the windowDidLoad method, you are reading the defaults and making the color well and check box reflect the current settings. In changeBackgroundColor: and changeNewEmptyDoc:, you are updating the defaults database.

You should now be able to build and run your application. It will read and write to the defaults database, so the Preferences panel will display the last color you chose and indicate whether the check box was on or off. You have not, however, done anything with this information yet, so the untitled document will continue to appear, and the background of the table view will continue to be white.

Using the Defaults

Now you are going to use the defaults. First, you will make your AppController become a delegate of the NSApplicationobject and suppress the creation of an untitled document, depending on the user defaults. Then, in MyDocument, you will set the background color of the table view from the user defaults.

Suppressing the Creation of Untitled Documents

As before, there are two steps to creating a delegate: implementing the delegate method and setting the delegate outlet to point to the object (Figure 13.4).

Figure 13.4. The delegate Suppresses Creation of Untitled Documents


Before automatically creating a new untitled document, the NSApplication object will send the messageapplicationShouldOpenUntitledFile: to its delegate. In AppController.m, add the following method:

- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender{
     NSLog(@"applicationShouldOpenUntitledFile:");
    return [[NSUserDefaults standardUserDefaults]
                                   boolForKey:BNREmptyDocKey];
}

To make your AppController the delegate of the NSApplication object, open the MainMenu.nib file, and Control-click File's Owner—which represents the NSApplication object—to bring up its connection window. Drag from delegate to yourAppController (Figure 13.5).

Figure 13.5. Select the delegate Outlet


Setting the Background Color on the Table View

After the nib file for a new document window has been successfully unarchived, your MyDocument object is sent the messagewindowControllerDidLoadNib:. At that moment, you can update the background color of the table view.

You should already have this method in MyDocument.m; simply edit it to look like this:

- (void)windowControllerDidLoadNib:(NSWindowController *)aController{
    [super windowControllerDidLoadNib:aController];
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *colorAsData;
    colorAsData = [defaults objectForKey:BNRTableBgColorKey];
    [tableView setBackgroundColor:
           [NSKeyedUnarchiver unarchiveObjectWithData:colorAsData]];
}

Also, make sure that you import PreferenceController.h at the beginning of MyDocument.m so that you can use the global variables that are declared there.

Build and run your application.


For the More Curious: NSUserDefaultsController

Sometimes, you will want to bind to a value from the NSUserDefaults object. An NSUserDefaultsController class makes this possible. All the nibs in your application will use a single shared instance of NSUserDefaultsController.

For example, if you wanted to use bindings, instead of target/action, to deal with the check box on the Preferences panel, you would bind it to the shared NSUserDefaultsController's value.EmptyDocumentFlag (Figure 13.6).

Figure 13.6. Binding to the NSUserDefaultsController

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值