iPhone和其他ios设备中有一个专用的Settings应用程序。通过Settings应用程序,用户可以输入和更改任何带有设置束(settings bundle)的应用程序首选项。设置束是构建到应用程序中的一组文件,它告诉Settings应用程序,主应用程序希望从用户那里收集到哪些首选项。
在iOS的用户默认设置(User Defaults)机制下,Settings应用程序是一个通用的用户界面。用户默认设置时保存和获取首选项的系统的一部分。在iOS应用程序中,用户默认设置由NSUserDefaults类实现。
Settings应用程序根据给定应用程序内部的设置束内容来显示该应用程序的首选项。每个设置束必须包含一个名为Root.plist的属性列表,她定义根级首选项视图。当Settings应用程序启动时,它检查设置束的每个应用程序并为包含设置束的每个应用程序添加设置组。如果想要首选项包含任何子视图,则必须向设置束中添加属性列表,并为每个子视图添加一个Root.plist条目。
简单来说,想要在Settings应用程序中加入自己的应用程序设置,需要在自己的应用程序中创建设置束(Settings Bundle)。File --> New File --> Resource --> Settings Bundle。
创建完成后可以在项目窗口下看到一个Settings.bundle的项。展开该项可以看到一个Root.plist文件。在该plist文件里的根节点下可以看到一个键为PreferenceSpecifiers的数组。这个数组节点用于保存一组dictionary节点,每个Dictionary节点都代表用户可输入的一个首选项或用户可以访问的一个子视图。
每个Item下都会有一个Type键,Type键可以让Settings应用程序知道哪种类型的数据与此条目相关。也就是说Type定义了在Settings程序中显示的视图组件。如PSGroupSpecifier定义了一个分组表的开始(当遇到下一个PSGroupSpecifier才结束),PSTextFieldSpecifier定义了一个文本输入框。PSMultiValueSpecifier定义了一个多值字段(必须设置默认值DefaultValue)选择视图,其中Titles和Values是必须的数组,不难看出它们是分别用于存放界面上显示的内容和真正要用到的的内容。当然Type还有其他类型(Slider,Switch等)。
最后一个分组表中定义了一个子视图,类型为PSChildPaneSpecifier。点击它将进入下一个设置视图。
其中主要就是这个File键,它对应的一个叫More.plist的文件,格式和Root.plist相同,配置方式完全一样。
除了Type键外,其他键根据其名字不难看出其代表的意思。重点注意一下这个key,该键对应的值用于我们在自己的应用程序中在NSUserDefault中取值。在应用程序中取值之前和设置之后可以通过调用 [[NSUserDefaults standardUserDefaults] synchronize]; 方法来同步数据。
完成上面内容就能在Settings程序中我们自己的应用的设置束了。但是需要注意的是,若应用程序仅仅是在后台运行,但并未关闭。这个时候用户去修改了Settings中的设置,需要通过应用程序的UIApplicationWillEnterForegroundNotification通知来调用我们的更新设置方法。所以我们要为我们的应用程序的控制器订阅该通知,并在视图销毁时移除该通知。
下面列出部分代码:
// // BIDMainViewController.h // AppSettings // // Created by ALEX on 13-4-8. // Copyright (c) 2013年 板鞋飞人. All rights reserved. // #import "BIDFlipsideViewController.h" #define kUsernameKey @"username" #define kPasswordKey @"password" #define kProtocolKey @"protocol" #define kWarpDriveKey @"warp" #define kWarpFactorKey @"warpFactor" #define kFavoriteTeaKey @"favoriteTea" #define kFavoriteCandyKey @"favoriteCandy" #define kFavoriteGameKey @"favoriteGame" #define kFavoriteExcuseKey @"favoriteExcuse" #define kFavoriteSinKey @"favoriteSin" @interface BIDMainViewController : UIViewController <BIDFlipsideViewControllerDelegate> @property (weak, nonatomic) IBOutlet UILabel *usernameLabel; @property (weak, nonatomic) IBOutlet UILabel *passwordLabel; @property (weak, nonatomic) IBOutlet UILabel *protocolLabel; @property (weak, nonatomic) IBOutlet UILabel *warpDrivelLabel; @property (weak, nonatomic) IBOutlet UILabel *warpFactorLabel; @property (weak, nonatomic) IBOutlet UILabel *favoriteTeaLabel; @property (weak, nonatomic) IBOutlet UILabel *favoriteCandyLabel; @property (weak, nonatomic) IBOutlet UILabel *favoriteGameLabel; @property (weak, nonatomic) IBOutlet UILabel *favoriteExcuseLabel; @property (weak, nonatomic) IBOutlet UILabel *favoriteSinLabel; - (void)refreshFields; @end
1 // 2 // BIDMainViewController.m 3 // AppSettings 4 // 5 // Created by ALEX on 13-4-8. 6 // Copyright (c) 2013年 板鞋飞人. All rights reserved. 7 // 8 9 #import "BIDMainViewController.h" 10 11 @interface BIDMainViewController () 12 13 @end 14 15 @implementation BIDMainViewController 16 17 - (void)viewDidLoad 18 { 19 [super viewDidLoad]; 20 // Do any additional setup after loading the view, typically from a nib. 21 22 UIApplication *app = [UIApplication sharedApplication]; 23 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:app]; 24 } 25 26 - (void)viewDidUnload{ 27 [super viewDidUnload]; 28 self.usernameLabel = nil; 29 self.passwordLabel = nil; 30 self.protocolLabel = nil; 31 self.warpFactorLabel = nil; 32 self.warpDrivelLabel = nil; 33 self.favoriteTeaLabel = nil; 34 self.favoriteSinLabel = nil; 35 self.favoriteGameLabel = nil; 36 self.favoriteExcuseLabel = nil; 37 self.favoriteCandyLabel = nil; 38 [[NSNotificationCenter defaultCenter] removeObserver:self]; 39 } 40 41 - (void)applicationWillEnterForeground:(NSNotification *)notification{ 42 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 43 [defaults synchronize]; 44 [self refreshFields]; 45 } 46 47 - (void)didReceiveMemoryWarning 48 { 49 [super didReceiveMemoryWarning]; 50 // Dispose of any resources that can be recreated. 51 } 52 53 - (void)refreshFields{ 54 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 55 self.usernameLabel.text = [defaults objectForKey:kUsernameKey]; 56 self.passwordLabel.text = [defaults objectForKey:kPasswordKey]; 57 self.protocolLabel.text = [defaults objectForKey:kProtocolKey]; 58 self.warpDrivelLabel.text = [defaults boolForKey:kWarpDriveKey] ? @"Engaged" : @"Disabled"; 59 self.warpFactorLabel.text = [[defaults objectForKey:kWarpFactorKey] stringValue]; 60 self.favoriteCandyLabel.text = [defaults objectForKey:kFavoriteCandyKey]; 61 self.favoriteExcuseLabel.text = [defaults objectForKey:kFavoriteExcuseKey]; 62 self.favoriteGameLabel.text = [defaults objectForKey:kFavoriteGameKey]; 63 self.favoriteSinLabel.text = [defaults objectForKey:kFavoriteSinKey]; 64 self.favoriteTeaLabel.text = [defaults objectForKey:kFavoriteTeaKey]; 65 } 66 67 - (void)viewDidAppear:(BOOL)animated{ 68 [super viewDidAppear:animated]; 69 [self refreshFields]; 70 } 71 72 #pragma mark - Flipside View 73 74 - (void)flipsideViewControllerDidFinish:(BIDFlipsideViewController *)controller 75 { 76 [self refreshFields]; 77 [self dismissViewControllerAnimated:YES completion:nil]; 78 } 79 80 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 81 { 82 if ([[segue identifier] isEqualToString:@"showAlternate"]) { 83 [[segue destinationViewController] setDelegate:self]; 84 } 85 } 86 87 @end
1 // 2 // BIDFlipsideViewController.h 3 // AppSettings 4 // 5 // Created by ALEX on 13-4-8. 6 // Copyright (c) 2013年 板鞋飞人. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h> 10 11 @class BIDFlipsideViewController; 12 13 @protocol BIDFlipsideViewControllerDelegate 14 - (void)flipsideViewControllerDidFinish:(BIDFlipsideViewController *)controller; 15 @end 16 17 @interface BIDFlipsideViewController : UIViewController 18 19 @property (weak, nonatomic) id <BIDFlipsideViewControllerDelegate> delegate; 20 @property (weak, nonatomic) IBOutlet UISwitch *engineSwitch; 21 @property (weak, nonatomic) IBOutlet UISlider *warpFactorSlider; 22 23 - (void)refreshFields; 24 - (IBAction)engineSwitchTapped; 25 - (IBAction)warpSliderTouched; 26 27 - (IBAction)done:(id)sender; 28 29 @end
1 // 2 // BIDFlipsideViewController.m 3 // AppSettings 4 // 5 // Created by ALEX on 13-4-8. 6 // Copyright (c) 2013年 板鞋飞人. All rights reserved. 7 // 8 9 #import "BIDFlipsideViewController.h" 10 #import "BIDMainViewController.h" 11 12 @interface BIDFlipsideViewController () 13 14 @end 15 16 @implementation BIDFlipsideViewController 17 18 - (void)viewDidLoad 19 { 20 [super viewDidLoad]; 21 // Do any additional setup after loading the view, typically from a nib. 22 23 UIApplication *app = [UIApplication sharedApplication]; 24 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:app]; 25 26 [self refreshFields]; 27 } 28 29 - (void)viewDidUnload{ 30 [super viewDidUnload]; 31 self.engineSwitch = nil; 32 self.warpFactorSlider = nil; 33 [[NSNotificationCenter defaultCenter] removeObserver:self]; 34 } 35 36 - (void)applicationWillEnterForeground:(NSNotification *)notification{ 37 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 38 [defaults synchronize]; 39 [self refreshFields]; 40 } 41 42 - (void)refreshFields{ 43 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 44 self.engineSwitch.on = [defaults boolForKey:kWarpDriveKey]; 45 self.warpFactorSlider.value = [defaults floatForKey:kWarpFactorKey]; 46 } 47 48 - (IBAction)engineSwitchTapped{ 49 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 50 [defaults setBool:self.engineSwitch.on forKey:kWarpDriveKey]; 51 [defaults synchronize]; 52 } 53 54 - (IBAction)warpSliderTouched{ 55 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 56 [defaults setFloat:self.warpFactorSlider.value forKey:kWarpFactorKey]; 57 [defaults synchronize]; 58 } 59 60 - (void)didReceiveMemoryWarning 61 { 62 [super didReceiveMemoryWarning]; 63 // Dispose of any resources that can be recreated. 64 } 65 66 #pragma mark - Actions 67 68 - (IBAction)done:(id)sender 69 { 70 [self.delegate flipsideViewControllerDidFinish:self]; 71 } 72 73 @end
Root.plist
More.plist
可以从项目窗口中看到,我们把设置slider的最大值和最小值的图片不但加入了应用程序目录,还加入到了设置束目录中。因为设置束是属于Settings应用程序的沙盒范围,无法访问到我们自己程序中的图片,同理,我们自己的程序也无法访问到设置束文件夹中的东西,不信的同学可以试试直接将外部图片Settings.bundle束下面拖,xcode是不会有反应的,只能右键Settings.bundle --> Show in Finder,在Finder中选中Settings.bundle,然后点击右键选择Show Package Contents。最后再往该层文件夹中放入图片。