iOS開發:使用Settings Bundle爲程序添加自定義設置項

Settings Bundle是在自己的程序中建立的一組文件,利用它可以告訴設備中的Settings程序我們寫的程序有哪些設置項。用戶在Settings程序中設置好相關相關選項後回到我們自己的程序,自己的程序中的對應項也會發生相應的變化。

在iOS程序中,用戶默認項,即上面所說的設置項,是用NSUserDefaults類實現的。在NSUserDefaults類中使用關鍵字來讀取和存儲設置項的具體數據,就像NSDictionary類一樣,不同的是,NSUserDefaults類的數據是存儲在文件系統中的,而不是作爲一個對象實例放在內存中。

在這篇文章中,我們將創建一個程序,爲它添加和配置Settings Bundle,之後可以在Settings程序中顯示和配置相關選項。在Settings Bundle中使用plist文件來定義程序中允許的設置項,Settings程序會自動建立接口。Settings Bundle中的plist文件必須遵循特定的格式,不過Xcode會幫助我們遵循這種格式。

1、首先下載初始代碼。http://www.oschina.net/code/snippet_164134_10458

由於我們的重點是Settings Bundle的使用,那麼建立工程、實現頁面跳轉等等就不詳細說明了。

2、解壓Settings Bundle Test.zip,使用Xcode 4.3打開此工程,先打開FirstViewController.xib,使用IB向其中添加控件,如下圖:

然後爲這些標籤向FirstViewController.h中建立Outlet映射:

3、打開MoreViewController.xib,向其中添加控件,如下:

然後,向MoreViewController.h中,爲右邊的兩個Label以及UISlider與UISWitch控件創建Outlet映射,爲UISlider與UISWitch控件添加Action映射:

然後在Attribute Insepector中設置UISlider控件最小值、最大值和默認值分別是0、10和5:

將準備好的兩張圖片添加到工程中,在Attribute Insepector中設置UISlider控件的Min Image與Max Image:

4、接下來,爲程序建立Settings Bundle。依次選擇File — New — File…,找到Settings Bundle:

單擊Next,設置好名稱和分組:

然後單擊Create,則創建了Settings.bundle。

5、展開Settings.bundle,其中包含一個Root.plist。Settings程序中的顯示項就是從Root.plist中獲取的。

單擊Root.plist以打開它,在空白處單擊,選中Show Raw Keys/Values:

接下來,我們展開PreferenceSpecifiers那一項,刪除除了Group之外的其他項。

PreferenceSpecifiers這個對應於剛打開Settings程序時第一頁中該應用程序的一個標籤,如果此時運行程序,然後在模擬器返回桌面,再打開Settings程序,則其中會出現Settings Bundle Test這一項:

上圖中的Settings Bundle Test這一項就可以理解爲是從PreferenceSpecifiers獲取的。

6、展開PreferenceSpecifiers這一項,先向其中添加三行,每一行的設置如下:

此時運行程序,在Settings程序中單擊上圖中的Settings Bundle Test那一項,出現的頁面如下圖:

   

在Root.plist文件中,Item 0那一項的類型是PSGroupSpecifier,它表示一個分組,則它下面的每Item 1到Item 3都是在這一組,直到下一個PSGroupSpecifier出現。

Item 1與Item 2都是PSTextFieldSpecifier類型的,它們都是文本框,可以通過爲其添加子項限制這些文本框的輸入。

Item 3是一個多選框,可供選擇的值是由Titles與Values決定的,Titles與Values這兩項內容是一樣的,但是缺一不可。

7、之後,再向其中添加Item 4到Item 7,如下圖:

再運行程序,打開Settings程序看看:

   

8、之後,我們實現在上面左圖的下方出現一項“更多設置”,單擊此項跳轉到新的頁面。先創建一個plist文件,名稱爲MoreSettings.plist,創建方法就不說了吧,參考http://my.oschina.net/plumsoft/blog/42084第二步。注意現在只是創建到工程中而不是Settings.bundle中,想要加到Settings.bundle中還要其他操作,稍後詳解。

右擊Settings.bundle,選擇Show in Finder:

然後在Finder中右擊Settings.bundle,選擇“顯示包含內容”:

在打開的地方將MoreSettings.plist以及上面用到的兩張圖片拷貝進去。

9、編輯Settings.bundle中的MoreSettings.plist內容如下:

保存後,在Root.plist添加一行:

注意,Item 8的類型是通過選擇其子選項Type的類型改變的。

此時運行程序,Settings程序如下:

   

10、接下來都是代碼了。

10.1 在ViewController.h中添加代碼:

#define kUserName @"username"
#define kPassWord @"password"
#define kGender @"gender"
#define kFavouriteColor @"favouritecolor"
#define kFavouriteSeason @"favouriteseason"
#define kFavouriteSport @"favouritesport"
#define kTheSlider @"theslider"
#define kTheSwitch @"theswitch"

10.2 在FirstViewController.h與MoreViewController.h中分別聲明一個方法,名爲

- (void)refreshFields;

在FirstViewController.m中添加代碼如下:

- (void)refreshFields {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    self.userNameLabel.text = [defaults objectForKey:kUserName];
    self.passWordLabel.text = [defaults objectForKey:kPassWord];
    self.genderLabel.text = [defaults objectForKey:kGender];
    self.colorLabel.text = [defaults objectForKey:kFavouriteColor];
    self.seasonLabel.text = [defaults objectForKey:kFavouriteSeason];
    self.sportLabel.text = [defaults objectForKey:kFavouriteSport];
}

在MoreViewController.m中添加代碼如下:

- (void)refreshFields {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    self.theSwitch.on = [defaults boolForKey:kTheSwitch];
    self.slider.value = [defaults floatForKey:kTheSlider];
    self.sliderValueLabel.text = [[defaults objectForKey:kTheSlider]stringValue];
}

10.3 實現MoreViewController.m中的sliderTapped與switchTapped方法如下:

- (IBAction)sliderTapped:(id)sender {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setFloat:self.slider.value forKey:kTheSlider];
    self.sliderValueLabel.text = [[defaults objectForKey:kTheSlider] stringValue];
    [defaults synchronize];
}
- (IBAction)switchTapped:(id)sender {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setBool:self.theSwitch.on forKey:kTheSwitch];
    [defaults synchronize];
}

10.4 在FirstViewController.m與MoreViewController.m中@end之前都添加代碼:

- (void)applicationWillEnterForeground:(NSNotification *)notification {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults synchronize];
    [self refreshFields];
}

10.5 在FirstViewController.m與MoreViewController.m的viewDidLoad方法中都添加代碼:

[self refreshFields];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:app];

10.6 在FirstViewController.m與MoreViewController.m中的ViewDidUnload方法中都添加代碼:

[[NSNotificationCenter defaultCenter] removeObserver:self];

11.7 在ViewController.m的viewDidLoad方法中的[super viewDidLoad];之前添加代碼:

//註冊默認項
NSDictionary *defaults = [NSDictionary dictionaryWithObjectsAndKeys:
                          [NSNumber numberWithBool:YES], kTheSwitch,
                          [NSNumber numberWithFloat:5.0], kTheSlider,
                          nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];

11、運行,那麼Settings Bundle Test程序中的數據將會隨着Settings程序中的選項改變而改變,反過來也是。

例如,在Settings程序中設置如下:

   

返回Settings Bundle Test程序,顯示如下:

   

不過,第一次運行程序時,單擊“更多設置”按鈕,Slider與Switch是變形的,然後纔會顯示正確的值:

真是好事多磨啊。

剛運行程序,第一次單擊“更多設置”按鈕就會出現上圖情況。猜想是因爲剛開始ViewController中的moreViewController爲空,在頁面跳轉時才創建它。爲此,試試在ViewController.m的viewDidLoad方法的最後一條語句之前添加代碼:

self.moreViewController = [[MoreViewController alloc] initWithNibName:@"MoreViewController" bundle:nil]; 
self.moreViewController.viewController = self;

再運行試試,還是不行。那估計是跟頁面跳轉時的動態效果有關了。再把ViewController.m的switchViews方法修改如下:

- (void)switchViews {
    if (self.moreViewController.view.superview == nil) { 
        if (self.moreViewController == nil) { 
            self.moreViewController = [[MoreViewController alloc] initWithNibName:@"MoreViewController" bundle:nil]; 
            self.moreViewController.viewController = self;
        }
    } else { 
        if (self.firstViewController == nil) { 
            self.firstViewController = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
            self.firstViewController.viewController = self;
        } 
    }
    [UIView beginAnimations:@"View Flip" context:nil]; 
    [UIView setAnimationDuration:0.80]; 
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    if (self.moreViewController.view.superview == nil) { 
        [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES]; 
        [self.firstViewController.view removeFromSuperview];
        [self.view insertSubview:self.moreViewController.view atIndex:0]; 
    } else { 
        [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES]; 
        [self.moreViewController.view removeFromSuperview]; 
        [self.view insertSubview:self.firstViewController.view atIndex:0]; 
    } 
    [UIView commitAnimations]; 
}

再運行,問題總算解決了。

最終代碼:http://www.oschina.net/code/snippet_164134_10484


以下是原作者與提問者的交互,很有幫助,一起粘來供大家學習:

評論17

  • 溫少鋒
    1樓:溫少鋒 發表於 2012-06-12 17:57 回覆此評論
    你好、、
    下載的那個初始項目與我的IOS模擬器不是一個版本的、、
    運行不了啊、、
    怎麼辦?
  • 雙子座
    2樓:雙子座 發表於 2012-06-12 19:52 回覆此評論

    引用來自“溫少鋒”的評論

    你好、、
    下載的那個初始項目與我的IOS模擬器不是一個版本的、、
    運行不了啊、、
    怎麼辦?

    升級XCode吧
  • 溫少鋒
    3樓:溫少鋒 發表於 2012-06-13 10:45 回覆此評論
    請問一下第8步創建MoreSettings.plist文件的是Mac OS X下的Property List還是IOS下的Preperty List,還有,第9步編輯Settings.bundle中的MoreSettings.plist的內容的文字全部是自己打進去的嗎?跟Root.plist中怎麼不一樣。沒有自動生成的、在這方面Xcode4.2和4.3有區別嗎?謝謝、、
  • 溫少鋒
    4樓:溫少鋒 發表於 2012-06-13 11:10 回覆此評論

    引用來自“雙子座”的評論

    引用來自“溫少鋒”的評論

    你好、、
    下載的那個初始項目與我的IOS模擬器不是一個版本的、、
    運行不了啊、、
    怎麼辦?

    升級XCode吧

    呵呵、、
    我的是黑蘋果、、
    10.7.2的系統不支持4.3
    又不想浪費時間去弄系統、、
    所以還是算了吧、、謝謝
  • RiC
    5樓:RiC 發表於 2012-06-29 22:02 回覆此評論
    setting.bundle裏面拷貝不進去 新建的plist和先前的圖片都不能放進去,樓主怎麼放的
  • 雙子座
    6樓:雙子座 發表於 2012-06-29 23:41 回覆此評論

    引用來自“RiC”的評論

    setting.bundle裏面拷貝不進去 新建的plist和先前的圖片都不能放進去,樓主怎麼放的

    不是在Xcode裏拷貝的,而是Finder中
  • RiC
    7樓:RiC 發表於 2012-06-30 14:54 回覆此評論

    引用來自“雙子座”的評論

    引用來自“RiC”的評論

    setting.bundle裏面拷貝不進去 新建的plist和先前的圖片都不能放進去,樓主怎麼放的

    不是在Xcode裏拷貝的,而是Finder中

    恩 搞定 謝謝
  • cos於鵬飛
    8樓:cos於鵬飛 發表於 2012-07-05 18:30 回覆此評論
    爲啥創建Settings.bundle的時候不能重命名?我重命名的就都顯示不出來,沒有重命名而是使用系統默認命名的就成功了?
  • cos於鵬飛
    9樓:cos於鵬飛 發表於 2012-07-05 21:07 回覆此評論
    總結一下我的問題,有幾個問題補充一下還望解答
    <第4節>爲什麼創建Settings.bundle的時候不能重命名?,這裏系統有默認嗎?
    <第11.7節>爲什麼要把這個代碼加載[super viewDidLoad];之前?我實驗了一下,加載後面也是可以的,這裏有什麼規範或者什麼注意點嗎?
    <關於文章末尾的BUG>我仔細看了下這部分代碼的區別,你怎麼看待這個BUG?如下結論能否得出?:界面切換之前必須創建界面,否則會出現異常。
    但是很奇怪的是,在viewDidLoad()中添加了self.moreViewController的初始化之後,斷點你的程序啓動第一次翻頁操作,無論修改前還是修改後,你switchViews()下的if (self.moreViewController == nil)這一句都是走不進去的,因爲self.moreViewController 必然不爲空,那麼還會有區別呢?
  • 雙子座
    10樓:雙子座 發表於 2012-07-17 22:06 回覆此評論

    引用來自“cos於鵬飛”的評論

    總結一下我的問題,有幾個問題補充一下還望解答
    <第4節>爲什麼創建Settings.bundle的時候不能重命名?,這裏系統有默認嗎?
    <第11.7節>爲什麼要把這個代碼加載[super viewDidLoad];之前?我實驗了一下,加載後面也是可以的,這裏有什麼規範或者什麼注意點嗎?
    <關於文章末尾的BUG>我仔細看了下這部分代碼的區別,你怎麼看待這個BUG?如下結論能否得出?:界面切換之前必須創建界面,否則會出現異常。
    但是很奇怪的是,在viewDidLoad()中添加了self.moreViewController的初始化之後,斷點你的程序啓動第一次翻頁操作,無論修改前還是修改後,你switchViews()下的if (self.moreViewController == nil)這一句都是走不進去的,因爲self.moreViewController 必然不爲空,那麼還會有區別呢?

    1、手機啓動後,設置程序會找到每個程序的Settings.bundle,從而在設置程序中顯示。所以,必須是Settings.bundle,不能用其他的名稱
    2、我是習慣加在[super viewDidLoad];之前,大部分情況下隨便,不過也有例外的時候
    3、最後一段代碼,我印象中當時修改的是關於動態效果的代碼,而對代碼原來的結構沒有改變。產生區別的是動態效果的代碼。if (self.moreViewController == nil)是比較保險的寫法,我看一本英文書中寫的,當系統資源不足的時候,當前未使用的變量會被銷燬。都過了很久了,有點忘了原文是怎麼講的了

  • 新風作浪
    11樓:新風作浪 發表於 2012-07-20 16:54 回覆此評論
    請問下,爲什麼我的Root.plist文件裏面Item0裏Type不能改成PSGroupSpecifier,而且改完後編程Group
  • 路上的問候
    12樓:路上的問候 發表於 2012-07-26 16:48 回覆此評論
    爲什麼我的slider就是不能在設置裏顯示吖?
  • 路上的問候
    13樓:路上的問候 發表於 2012-07-27 17:02 回覆此評論
    您好,我解決了以上問題,但是還有個問題:
    問下:我下載了你的代碼,你代碼中是@class RootViewController;
    用按鈕實現了兩個視圖的切換,
    在FirstViewController.h中@class RootViewController;以及@property (strong, nonatomic) RootViewController *rootViewController_d;
    在FirstViewController.m中#import "RootViewController.h"以及@synthesize rootViewController_d;
    然後通過按鈕的方法調用了RootViewController.h/.m中的switchViewsAction方法:
    - (IBAction)switchBTNaction:(id)sender{
      [self.rootViewController_d switchViewsAction];
      //NSLog(@"aaaaaa");
      }
    我調試怎麼也進不了RootViewController.m中的switchViewsAction方法?????????
    但是不提示錯誤呀。

    我想不明白:FirstViewController.m只是聲明一個RootViewController *rootViewController_d;
    是在哪裏真正附值呢?
    我調試的也是:
    rootViewController_d  RootViewController *  0x00000000
  • 路上的問候
    14樓:路上的問候 發表於 2012-07-27 17:27 回覆此評論
    嘻嘻,把問題寫出來了,確突然解決了。。
    是在RootViewController.m中有對rootViewController_d 的附值:
    self.secondViewController.rootViewController_second = self;
    self.firstViewController.rootViewController_first = self;
  • zk0301
    15樓:zk0301 發表於 2012-09-03 16:34 回覆此評論
    iphone的settings還有那麼古怪的功能,真心不習慣。管的太多了吧。汗~ 下一篇 衝啊
  • GlorySimba
    16樓:GlorySimba 發表於 2012-09-06 14:21 回覆此評論
    謝謝博主的分享!
  • HeAnd
    17樓:HeAnd 發表於 2012-10-06 17:09 回覆此評論
    成功,謝謝


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章