三十而立,從零開始學ios開發(二十):Application Settings and User Defaults(下)

來源:互聯網
上載者:User

在上一篇的學習中,我們知道了如何為一個App添加它的Settings設定項,在Settings設定項中我們可以添加哪些類型的控制項,這些控制項都是通過一個plist來進行管理的,我們只需對plist進行修改添加,就可以映射到Settings中。但是在上一篇中,我們並沒有學習Settings和App的互動,在這一篇中我們將進行學習,如何在一個App中讀取Settings中的值,如何在App中修改Settings中的值,好了,下面開始我們這次的學習。

1)NSUserDefaults
NSUserDefaults是ios內建的一個對象,它的主要作用對Settings中的變數(我們添加的控制項)進行取值和賦值。在上一篇中,我們在建立plist的時候,每一個Item都有一個Key,NSUserDefaults就是根據這個Key來找到對象,然後取得值,或者賦值。這個其實就是一個key-value對,NSUserDefaults在用法上和NSDictionary是一樣的,因此它也有很多類似的對象例如:objectForKey,intForKey,floatForKey,boolForKey,這些都很簡單。另外NSUserDefaults是一個單例模式(Singleton),也就是說在整個app中只有一個NSUserDefaults對象存在,這樣可以避免在2個地方同時對一個Item進行操作的情況,在程式中,我們使用standardUserDefaults方法來取得NSUserDefaults對象:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

2)從Settings中讀取值
開啟BIDMainViewController.h,添加如下代碼

#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 *warpDriveLabel;@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

上面的代碼定義了10個常量Key,用來在之後的代碼中根據Key擷取Settings中的值,接著聲明10個IBOutlet,且他們都是指向Label的,最後聲明了一個refreshField方法,用於從Settings中讀取值然後賦給Label。

儲存上面的code,然後在Project navigator中選中MainStoryboard.storyboard,在Layout area中會顯示2個View,分別是Main View和FlipSide View,我們選中Main View,然後開啟Attribute inspector,找到Backgrund,將其背景色變為白色

在Main View的右下角,有一個Info button(一個圓圈,中間有一個字母i),選中它,然後在Attribute inspector中將其Type改成“Info Dark”

這樣就能夠很容易的找到它所在的地方了

如果你覺得直接在Main View中選擇一個白色的Info button很困難,那麼你可以開啟Main View Controller Scene,在裡面你可以方便的找到它

好,接下來我們就要往Main View中拖控制項了,一共拖20個Label,其中十個Label為靜態,僅僅顯示文字,另外十個需要與剛才定義的IBOutlet關聯起來,用於顯示Settings中的值。

根據的樣子,將Label拖入到Main View中

排列什麼的無所謂,自己喜歡就好,主要是右邊的10個Label,他們的寬度都是到最右邊出現輔助線的位置,以保證能夠最大限度容納字元。

好了,下面的工作就是關聯IBOutlet和view上面的Label了,一共要關聯10個Label,選中Main View Controller Scene中的Main View Control,control-drag到Label上(右邊的一排Label),在彈出的框中選中相對應的IBOutlet

好了,儲存所有的修改,下面開始編碼。

開啟BIDMainViewController.m,添加以下code

@implementation BIDMainViewController- (void)refreshFields {    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];    self.usernameLabel.text = [defaults objectForKey:kUsernameKey];    self.passwordLabel.text = [defaults objectForKey:kPasswordKey];    self.protocolLabel.text = [defaults objectForKey:kProtocolKey];    self.warpDriveLabel.text = [defaults boolForKey:kWarpDriveKey] ? @"Enabled" : @"Disabled";    self.warpFactorLabel.text = [[defaults objectForKey:kWarpFactorKey] stringValue];    self.favoriteTeaLabel.text = [defaults objectForKey:kFavoriteTeaKey];    self.favoriteCandyLabel.text = [defaults objectForKey:kFavoriteCandyKey];    self.favoriteGameLabel.text = [defaults objectForKey:kFavoriteGameKey];    self.favoriteExcuseLabel.text = [defaults objectForKey:kFavoriteExcuseKey];    self.favoriteSinLabel.text = [defaults objectForKey:kFavoriteSinKey];}- (void)viewDidAppear:(BOOL)animated {    [super viewDidAppear:animated];    [self refreshFields];}

首先我們實現了refreshFields方法,裡面首先聲明了NSUserDefaults,用於擷取Settings中的值,之後調用objextForKey,通過Key來擷取10個Label的值,有2個比較特殊,第一個是kWarpDriveKey,由於WarpDrive是一個Switch,它返回的是一樣bool型,因此通過三元運算子來判斷YES或NO,如果是YES,返回“Enabled”,如果是NO,則返回“Disabled”。另一個比較特殊的是WarpFactor,由於WarpFactor是一個slider,返回的是一個int型,所以通過stringValue將int型轉換為string輸出。

然後我們重載了viewDidAppear方法,在view出現時調用refreshFields方法。

接著在BIDMainViewController.m中找到flipsideViewControllerDidFinish方法,添加如下code

- (void)flipsideViewControllerDidFinish:(BIDFlipsideViewController *)controller{    [self refreshFields];    [self dismissViewControllerAnimated:YES completion:nil];}

在Main View的右下角有一個info button,點擊它會切換到flipsideView,當從flipsideView切換回來的時候,就會調用上面的這個方法,然後再次重新整理Settings中的資料。

好了,編譯運行一下程式,程式啟動後,在Settings中為AppSettings中的每一項賦值,然後開啟AppSettings程式,就可以看到一下類似的了。

 

3)將值寫入Settings
剛才我們通過NSUserDefaults從Settings讀取了值,現在我們將學習如何在程式中設定值,然後再寫入到Settings中。

開啟MainStoryboard.storyboard,在layout area中,將Flipside View Controller布局成如下樣子

將View的背景色改成Light Gray color,View的title改成“Warp Settings”,添加了2個Label,添加了一個Switch和Slider,其中,Slider的左右兩個圖片是在其Attributs inspector中設定的

然後將slider的最小值設為1,最大值設為10,當前值設為5,同樣也是在Attributes inspector中進行設定

根據View中的內容,我們可以看出是針對Settings中的switch(WarpDrive)和slider(Warp Factor),開啟BIDFlipsideViewController.h,添加如下code

#import <UIKit/UIKit.h>@class BIDFlipsideViewController;@protocol BIDFlipsideViewControllerDelegate- (void)flipsideViewControllerDidFinish:(BIDFlipsideViewController *)controller;@end@interface BIDFlipsideViewController : UIViewController@property (weak, nonatomic) id <BIDFlipsideViewControllerDelegate> delegate;@property (weak, nonatomic) IBOutlet UISwitch *engineSwitch;@property (weak, nonatomic) IBOutlet UISlider *warpFactorSlider;- (void)refreshFields;- (IBAction)engineSwitchTapped;- (IBAction)warpSliderTouched;- (IBAction)done:(id)sender;@end

兩個IBOutlet自然是指向View上的Switch和Slider的,refreshFields是用來重新整理Flipside上的資料的,2個IBAction,當swtich發生改變時,觸發engineSwitchTapped事件,當slider發生改變時,觸發warpSliderTouched事件。

下面就是綁定了,展開Flipside View Controller Scene

然後選中Flipside View Controller根節點,control-drag到switch上,在彈出的框中選擇engineSwitch
同樣的方法,control-drag到slider上,選中warpFactorSlider

然後綁定事件,這次在Flipside View Controller選中switch,然後control-drag到Flipside View Controller Scene中的根節點Flipside View Controller上,會彈出一個框

選中enginSwitchTapped,同學選擇slider,control-drag,選擇warpSliderTouched

好了,介面上的操作全部完成了,下面開始編碼,開啟BIDFlipsideViewController.m,添加如下代碼

- (void)viewDidLoad{    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    [self refreshFields];}......- (void)refreshFields{    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];    self.engineSwitch.on = [defaults boolForKey:kWarpDriveKey];    self.warpFactorSlider.value = [defaults floatForKey:kWarpFactorKey];}- (IBAction)engineSwitchTapped{    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];    [defaults setBool:self.engineSwitch.on forKey:kWarpDriveKey];}- (IBAction)warpSliderTouched{    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];    [defaults setFloat:self.warpFactorSlider.value forKey:kWarpFactorKey];}

首先在viewDidLoad中調用refreshFields的方法,接著實現了refreshFields,用於讀取Settings中的值並顯示在介面上,然後實現了2個IBAction,第一個用於儲存Swtich的值,另一個儲存Slider的值,2個方法都很簡單,看代碼就能看懂了。

好,儲存編譯運行程式,在主介面中點擊右下角的info button,切換到flip view

flip view上顯示了Settings中Warp Drive的值和Warp Factor的值,然後我們調整一下,將Warp Engines設為ON,然後改變一下Warp Factor的位置,例如

然後按左上方的Done,返回main view,可以看到Warp Drive和Warp Factor的值也相應發生了改變

3)設定預設值
好了,到目前位置,我們已經可以做很多事情了,在App中讀取Settings中的值,在App中改變Settings中的值並儲存回Settings中,但是大家有沒有發現,當你們第一運行app時,main view中其實是空的,什麼都沒有,只有當我們退出程式,在Settings中設定好值以後,再回到App中,此時main view中的值才會有。其實這個情況是正確的,但是我們更希望能夠有一個預設值存在,我們無法在Settings.bundle中設定預設值,但是NSUserDefaults為我們提供了方法,開啟BIDAppDelegate.m,添加如下代碼

#import "BIDAppDelegate.h"#import "BIDMainViewController.h"@implementation BIDAppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{    // Override point for customization after application launch.    NSDictionary *defaults = @{kWarpDriveKey: @YES,                               kWarpFactorKey: @5,                               kFavoriteSinKey: @"Greed"};        [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];    return YES;}

從代碼中可以看到,NSUserDefaults通過registerDefaults方法設定預設值,它的參數是一個NSDictionary,在NSDictionary設定了3個對象的預設值。好了,在重新編譯運行程式之前,請確保將模擬器中的AppSettings刪除,再編譯運行,效果如下

可以看到,Warp Drive, Warp Factor, Favorite Sin三個對象都有預設值,和我們的預期是一樣的。

4)資料的同步
相信大家現在的手機至少是ios4以上的版本了,從ios4開始,ios就支援多任務了(雖然表面上是支援的),使用者可以在多個app之間進行切換。我們這個app當然也可以,但是有一個問題,我們運行程式,app預設介面如下

接著按Home鍵,回到案頭進入Settings,為Username

然後退出Settings,再進入AppSettings,你會發現剛才輸入的username並沒有顯示出來

ok,好,我們現在就來解決這個問題。首先簡單的說一下原理,這裡用到了ios的一個機制:Notification。當一個背景程式被啟用後,它將收到一個notification:UIApplicationWillEnterForegroundNotification,表示app即將顯示到前台來。我們首先定義一個方法,當發生notification時會調用該方法,分別開啟BIDMainViewController.m和BIDFlipsideViewController.m,分別添加如下代碼

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

NSUserDefaults的synchronize方法是用來同步Settings中的值的,當值同步完後,調用refreshFields方法,顯示到view中。

再分別為BIDMainViewController.m和BIDFlipsideViewController.m添加如下方法

- (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear:animated];        UIApplication *app = [UIApplication sharedApplication];    [[NSNotificationCenter defaultCenter] addObserver:self                                             selector:@selector(applicationWillEnterForeground:)                                                 name:UIApplicationWillEnterForegroundNotification                                               object:app];}

該方法用於監聽ios發出的notification,當監聽到UIApplicationWillEnterForegroundNotification通知後,就調用applicationWillEnterForeground方法。

我們還要更新2個已經寫好的方法,engineSwitchTapped和warpSliderTouched(在BIDFlipsideViewController.m中),修改如下

- (IBAction)engineSwitchTapped{    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];    [defaults setBool:self.engineSwitch.on forKey:kWarpDriveKey];    [defaults synchronize];}- (IBAction)warpSliderTouched{    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];    [defaults setFloat:self.warpFactorSlider.value forKey:kWarpFactorKey];    [defaults synchronize];}

新增了同步,每當值發生改變,就同步一下。

我們最後還要添加一個方法,用於釋放notification,還是分別為BIDMainViewController.m和BIDFlipsideViewController.m添加如下方法

- (void)viewDidDisappear:(BOOL)animated{    [super viewDidDisappear:animated];    [[NSNotificationCenter defaultCenter] removeObserver:self];}

當view消失後(進入後台或者程式退出),將notification釋放。

好了,再次編譯運行app,按Home鍵回到案頭,進入Settings修改值再回到AppSettings,會發現相對於的值發生了改變。

5)總結
應該說,這篇以及上一篇關於Settings的內容都已經講完了,涵蓋了絕大部分關於Settings的操作,這部分的內容在程式設計開發的過程中還是相對有用的,對我來說收穫很大。下一篇開始,我們將重點講解ios對於資料的儲存功能是如何?的,又是一個很有用的內容,每個app幾乎都會使用到,有些儲存的方法其實我們已經學習過了,在下一章會重點再介紹一次,希望下一篇會很快到來,我會努力的!

AppSettings_all.zip

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.