iOS Programming NSUserDefaults
iOS Programming NSUserDefaults
When you start an app for the first time, it uses its factory settings. As you use it, a good app learns your preferences. Where are your preferences stored? Inside each app bundle there is a plist that holds the user's preferences. As a developer, you will access this plist using the NSUserDefaults class. The preferences plist for your app can also be edited by the Settings app. To allow this, you create a settings bundle inside your app.
当你第一次开始你的app时,你使用它的工厂设置。当你使用它时,一个好的app 会学习你的preferences.你的preferences 存在哪了呢?在每个app bundle中都有一个plist 保持了user的preference.作为一个developer,你可以通过使用NSUserDefaults类。preferences plist 你的app能够通过setting app 编辑。为了允许这个,你需要创建一个setting bundle 在你的app内。
1 NSUserDefaults
The set of defaults for a user is a collection of key-value pairs. The key is the name of the default, and the value is some data that represents what the user prefers for that key.You ask the shared user defaults object for the value of that key – not unlike getting an object from a dictionary:
对一个用户的defaults 的设置是一个key-value pairs 容器。key 是default的名字,而value是代表饿了用户喜欢什么的数据。你询问shared user defaults 对象为了那个key 的value,很像从一个字典中获取对象。
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *greeting = [defaults objectForKey:@"FavoriteGreeting"];
If the user expresses a preference, you can set the value for that key:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"Hello" forKey:@"FavoriteGreeting"];
This value will automatically be stored to the app's preferences plist. Thus, the value must be a plist type: NSArray, NSDictionary, NSString, NSData, NSDate, or NSNumber. If you want to store a non-plist type to the user defaults, you will need to convert it to a plist. Often this is accomplished by archiving the object (or objects) into an NSData, which is a plist.
这些值自动的存入app的preference plist.因此这个value必须是plist type:NSArray,NSDictionary,NSString,NSData,NSDate,or NSNumber.如果你是存的一个non-plist,你需要把它转换为plist。通常这会存档一个对象为NSData。
What if you ask for the value of a preference that has not been set by the user? NSUserDefaults will return the factory settings, the "default default," if you will. These are not stored on the file system, so you need to tell the shared instance of NSUserDefaults what the factory settings are every time your app launches. And you need to do it early in the launch process – before any of your classes try to read the defaults. Typically you will override +initialize on your app delegate:
如果你询问一个preference的值还没有被设置?NSUserDefaults将会返回factory settings ,the "default default",如果你乐意。这些没有存储到文件系统中,所以你需要告诉NSUserDefaults 的shared instance 在你的app 每次启动时要设置什么factory settings .你需要做这些在启动程序的早期:在你的类视图读取defaults之前。一般你回重+initialize 在你的app delegate上。
+ (void)initialize
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *factorySettings = @{@"FavoriteGreeting": @"Hey!",
@"HoursBetweenMothershipConnection : @2};
[defaults registerDefaults:factorySettings];
}
The class method initialize is called automatically by the Objective-C runtime before the first
instance of that class is created.
这个类方法initialize 在那个类被创建的Objective -C 运行时间自动的被调用。
1.1 Register the factory settings
注册factory Settings
At launch time, the first thing that will happen is the registering of the factory settings. It is considered good style to declare your preference keys as global constants.
启动时,将发生的第一件事就是factory settings 的注册。声明你的preference keys 为一个global constants 是一种好的方式。Open BNRAppDelegate.h and declare two constant global variables:
extern NSString * const BNRNextItemValuePrefsKey;
extern NSString * const BNRNextItemNamePrefsKey;
In BNRAppDelegate.m, define those global variables and use them to register the factory defaults in
+initialize:
NSString * const BNRNextItemValuePrefsKey = @"NextItemValue";
NSString * const BNRNextItemNamePrefsKey = @"NextItemName";
@implementation BNRAppDelegate
+ (void)initialize
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *factorySettings = @{BNRNextItemValuePrefsKey: @75,
BNRNextItemNamePrefsKey: @"Coffee Cup"};
[defaults registerDefaults:factorySettings];
}
1.2 Read a preference 读取一个preference
When you create a new item in BNRItemStore.m, use the default values. Be sure to import BNRAppDelegate.h at the top of BNRItemStore.m so that the compiler knows about BNRNextItemValuePrefsKey.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
item.valueInDollars = [defaults integerForKey:BNRNextItemValuePrefsKey];
item.itemName = [defaults objectForKey:BNRNextItemNamePrefsKey];
// Just for fun, list out all the defaults
NSLog(@"defaults = %@", [defaults dictionaryRepresentation]);
Notice the method integerForKey:. It is there as a convenience. It is equivalent to:
item.valueInDollars = [[defaults objectForKey:BNRNextItemValuePrefsKey] intValue];
There are also convenience methods for setting and getting float, double, BOOL, and NSURL values.
1.3 Change a preference
You could create a view controller for editing these preferences. Or, you could create a settings bundle for setting these preferences. Or, you can just try to guess the user's preferences from their actions. For example, if the user sets the value of an item to $100, that may be a good indication that the next item might also be $100. For this exercise, you will do that.
你可以创建一个view controller 来编辑这些preferences.或者,你能创建settings bundle 来设置这些preferences.或者你仅仅视图猜测用户的preferences 从他们的actions。例如,一个用户设置一个item的value是100,那么这是一个很好的indication ,next item 可能也是100。
Open BNRDetailViewController.m and edit the viewWillDisappear: method.
int newValue = [self.valueField.text intValue];
// Is it changed?
if (newValue != item.valueInDollars) {
// Put it in the item item.valueInDollars = newValue;
// Store it as the default value for the next item
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:newValue forKey:BNRNextItemValuePrefsKey];
item.valueInDollars = [self.valueField.text intValue];
}
Import BNRAppDelegate.h so that the compiler knows about the BNRNextItemValuePrefsKey constant.
Sometimes you will give the user a button that says "Restore factory default", which will remove some defaults from the app's preferences plist.
To remove key-value pairs from your app's preferences plist, NSUserDefaults has a removeObjectForKey: method.
有时候你提供给用户一个button 说重新设置factory default,这将移除所有的default在你的app的preferences plist.移除所有的key-value pairs 从你的app's preferences plist,NSUserDefaults 有一个removeObjectForKey方法。
2 Settings Bundle
Now you are going to create a settings bundle so that the NextItemName preference can be changed from "Coffee Cup" to whatever string the user desires.
现在你将创建一个settings bundle 这样NextItemName preference 能从"Cofffee Cup"到任何用户需要的数据。
These days many designers consider settings bundles to be distasteful and most apps do not include a settings bundle. That said, many apps do have settings bundles, so it is a good idea to know how to make them.
这些天,许多designers 认为setting bundles 为不好的,许多apps 不包括setting bundles .也就是许多apps 有settings bundles ,所以知道怎么用她是有用的。
The bundle is just a directory that holds a plist that describes what controls should appear in this view and what default each control is manipulating.
bundle 仅仅是拥有了一个plist表示了什么控制应该出现在这个view和每个控制默认的操作是什么的一个目录而已。
You will pull the user visible strings (like the label "Default Item Name" in
Figure 26.1) into a strings file that is localized for the user. Those strings files will also be in the settings bundle.
你将把user visible strings 放进一个Strings file用来用户本地化。这些Strings files 也将在settings bundle.
To create a settings bundle inside your app, open Xcode's File menu and choose New → File.... Under the iOS Resources pane, choose Settings Bundle
Accept the default name. Notice that a directory called Settings.bundle has been created in your project directory. It has a Root.plist file and an en.lprog subdirectory.
1.1 Editing the Root.plist
The Root.plist describes what controls will appear in your app's settings pane. It contains an array of dictionaries; each dictionary represents one view (typically a control) that will appear on the pane. Every dictionary must have Type key. Here are the acceptable values for Type:
Root.plist表述了什么控制将显示到你的app's setting pane。它包含了一列dictionaries;每个dictionary 代表了一个view将显示在pane上的。每个dictionary 必须有一个Type key。这里是一些能接受的值:
PSTextFieldSpecifier
a labeled text field 标签文字
PSToggleSwitchSpecifier
a labeled toggle switch 开关标签
PSSliderSpecifier
a slider (not labeled) 滑动
PSRadioGroupSpecifier
a list of radio buttons; only one can be selected 一列radio buttons,只有一个可选
PSMultiValueSpecifier
a table view of possibilities; only one can be selected 一个table view 的可能
PSTitleValueSpecifier
a title for formatting
PSGroupSpecifier
a group for formatting
PSChildPaneSpecifier
lets you move some preferences onto a child pane
让你移动一些preferences 到child pane.
Notice that you have been laying out a user interface using a plist file. When you create a settings bundle, you are not writing any executable code, and you are not creating any view controllers or other objects that you control. The Settings application will read your application's Root.plist and will construct its own view controllers based on the contents of the plist file.
注意到你已经laying out 一个user interface用一个plist file.当你创建一个setting bundle时,你没有写任何可执行的代码,你也没有创建然任何view controller 或者是你控制的其他对象。setting application 会读取你应用的root.plist并构建自己的view controller 基于plist file的内容。
2.2 Localized Root.strings
Inside your settings bundle is an en.lproj which will hold your English strings. You can delete all the key-value pairs and give the title for your text field:
在你的setting bundle是一个en.lproj,它将保持你的English strings.你可以删除所有的key-value pairs 并给定你的text field 的title。
"NextItemName" = "Default Item Name";
One final point: when your defaults are changed (by your own app or the Settings app) an NSUserDefaultsDidChangeNotification will get posted to your application. If you want to respond to changes in the Settings app immediately, register as an observer of this notification.
最后一点是:当你的defaults 发生改变时,NSUserDefaultsDidChangeNotification 将推送给application。如果你想及时的响应setting app 立刻的,注册成为这个notification 的一个观察者。