iOS對UIViewController生命週期和屬性方法的解析

iOS對UIViewController生命週期和屬性方法的解析

1、引言

        做爲MVC設計模式中的C,Controller一直扮演着項目開發中最重要的角色,它是視圖和數據的橋樑,經過它的管理,將數據有條有理的展現在咱們的View層上。iOS中的UIViewController是UIKit框架中最基本的一個類。從第一個UI視圖到複雜完整項目,都離不開UIViewController做爲基礎。基於UIViewController的封裝和擴展,也可以出色的完成各類複雜界面邏輯。這篇博客,旨在討論UIViewController的生命週期和屬性方法,在最基礎的東西上,每每會獲得意想不到的驚喜。設計模式

2、UIViewController的生命週期

        要了解UIViewController,先要弄清楚其生命週期。在面向對象的語言中,是對象,就必定要有生命週期,UIViewController也不例外,生命週期管理Controller的做用範圍和時間,也管理其內對象的做用範圍和時間。首先,UIViewController中與其生命週期有關的幾個函數以下:數組

//類的初始化方法
+ (void)initialize;
//對象初始化方法
- (instancetype)init;
//從歸檔初始化
- (instancetype)initWithCoder:(NSCoder *)coder;
//加載視圖
-(void)loadView;
//將要加載視圖
- (void)viewDidLoad;
//將要佈局子視圖
-(void)viewWillLayoutSubviews;
//已經佈局子視圖
-(void)viewDidLayoutSubviews;
//內存警告
- (void)didReceiveMemoryWarning;
//已經展現
-(void)viewDidAppear:(BOOL)animated;
//將要展現
-(void)viewWillAppear:(BOOL)animated;
//將要消失
-(void)viewWillDisappear:(BOOL)animated;
//已經消失
-(void)viewDidDisappear:(BOOL)animated;
//被釋放
-(void)dealloc;

上面這麼多的函數,乍一看什麼複雜,其實關係什麼明朗,除了initialize,init和initWithCoder不是存在全部對象的聲明週期中,其餘函數都會在UIViewController的聲明週期中有序的被調用。那麼具體的調用順序是怎樣的呢,最好的辦法是實踐一下,經過編號打印,結果以下:app

這是一個ViewController完整的聲明週期,其實裏面還有好多地方須要咱們注意一下:框架

1:initialize函數並不會每次建立對象都調用,只有在這個類第一次建立對象時纔會調用,作一些類的準備工做,再次建立這個類的對象,initalize方法將不會被調用,對於這個類的子類,若是實現了initialize方法,在這個子類第一次建立對象時會調用本身的initalize方法,以後不會調用,若是沒有實現,那麼它的父類將替它再次調用一下本身的initialize方法,之後建立也都不會再調用。所以,若是咱們有一些和這個相關的全局變量,能夠在這裏進行初始化。dom

2:init方法和initCoder方法類似,只是被調用的環境不同,若是用代碼進行初始化,會調用init,從nib文件或者歸檔進行初始化,會調用initCoder。iphone

3:loadView方法是開始加載視圖的起始方法,除非手動調用,不然在ViewController的生命週期中沒特殊狀況只會被調用一次。ide

4:viewDidLoad方法是咱們最經常使用的方法的,類中成員對象和變量的初始化咱們都會放在這個方法中,在類建立後,不管視圖的展示或消失,這個方法也是隻會在將要佈局時調用一次。函數

5:viewWillAppear:視圖將要展示時會調用。佈局

6:viewWillLayoutSubviews:在viewWillAppear後調用,將要對子視圖進行佈局。測試

7:viewDidLayoutSubviews:已經佈局完成子視圖。

8:viewDidAppare:視圖完成顯示時調用。

9:viewWillDisappear:視圖將要消失時調用。

10:viewDidDisappear:視圖已經消失時調用。

11:dealloc:controller被釋放時調用。

注意:通過測試,從nib文件加載的controller,只要不釋放,在每次viewWillAppare時都會調用layoutSubviews方法,有時甚至會在viewDidAppare後在調用一次layoutSubviews,而重點是從代碼加載的則只會在開始調用一次,以後都不會,因此注意,在layoutSubviews中寫相關的佈局代碼十分危險。

3、從storyBoard加載UIViewController實例的傳值陷阱

        咱們知道,當咱們從StoryBoard中加載ViewController時,咱們在Controller中拖拽的視圖是能夠被初始化的,這裏面有一點須要咱們注意,若是咱們須要向controller中視圖進行傳值設置,經過如下方法獲得的Controller中,視圖尚未被初始化建立出來:

 ViewController2 * viewController2 = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"ViewController2"];

咱們能夠在ViewController2的storyBoard中拉一個label,而後關聯到頭文件中,以下打印,會發現咱們獲得controller時,裏面的視圖對象並無進行建立:

ViewController2 * viewController2 = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"ViewController2"];
    NSLog(@"%@",viewController2.label);
    [self presentViewController:viewController2 animated:YES completion:nil];

打印以下:

能夠想象,若是咱們這時候須要對label進行一些屬性設置,必然失敗。有人提出能夠在建立後,手動調如下loadView方法,咱們試一下,結果以下:

能夠看到,手動調用loadView後,label是被建立了出來,可是暴漏了一個更嚴重的問題,系統不在調用ViewDidLoad方法,這是十分有風險的,由於咱們大部分的初始化代碼都會放在這個方法裏,因此手動調用loadView是一種錯誤的方法,apple文檔聲明對於loadView方法,咱們歷來都不要手動直接調用,那麼咱們如何實現建立後對成員對象進行傳值設置呢,iOS9中增長了這樣一個方法:

- (void)loadViewIfNeeded NS_AVAILABLE_IOS(9_0);

這個方法十分有用,調用這個方法,會將視圖建立出來,而且不會忽略viewDidLoad的調用。

在iOS9中,UIViewController還增長了下面一個布爾值的屬性,能夠同來判斷controller的view是否已經加載完成:

@property(nullable, nonatomic, readonly, strong) UIView *viewIfLoaded NS_AVAILABLE_IOS(9_0);

4、UIViewController與StroyBoard的相關相互方法

        對於ViewConroller,咱們通常有兩種方式建立,一種是用純代碼的方式,一種是與StoryBoard關聯,在UIViewController中,有許多方法方便咱們與StoryBoard進行交互聯繫。

一、ViewController直接在StoryBoard中進行跳轉的傳值

        在StoryBoard中進行界面跳轉是十分方便的,咱們在StoryBoard中拉入兩個ViewController,在一個上面添加一個按鈕,點住按鈕按住control,將鼠標拉到第二個controller上,會出現以下的跳轉選項:

咱們選擇一個後,就會在兩個controller之間創建一個跳轉鏈接。當咱們運行點擊按鈕後,會自動從第一個controller跳轉到第二個controller。在UIViewController中有以下方法能夠對是否跳轉進行控制:

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(nullable id)sender NS_AVAILABLE_IOS(6_0);

這個方法若是返回NO,自動跳轉將不能進行,會被拒絕,須要注意的是,這個方法只會在自動的跳轉時被調用,咱們手動使用代碼跳轉StoryBoard中的鏈接關係時是不會被調用的,咱們後面討論。

        在執行過上述方法後,若是返回YES,系統還會在執行以下一個方法,做爲跳轉前的準備,咱們能夠在這個方法中進行一些傳值操做,這個方法不管使咱們手動進行跳轉仍是storyboard中自動跳轉,都會被執行:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(nullable id)sender NS_AVAILABLE_IOS(5_0);

sugur對象中封裝了相關的ViewController,可使用segue.destinationViewController獲取。

        segue在StoryBoard中除了用來自動正向跳轉外,咱們還能夠進行反向的跳轉,相似pop和dismiss方法,這種segue被稱爲unwind sugue。例如,咱們有一個controller1和一個controllert2,要使用unwind segue從2返回1,咱們須要在2中實現以下格式的方法:

- (IBAction)unwindSegueToViewController:(UIStoryboardSegue *)segue {
    NSLog(@"unwindSegueToViewController");
}

這個方法中的返回值必須爲IBAction,參數必須是UIStoryboardSegue,方法名咱們能夠本身定義,以後在StoryBoard中的ViewController1中的Exit選項中,咱們會發現多了一個這樣的方法:

咱們能夠把它鏈接到viewController2中的一個按鈕上:

這樣,當咱們點擊viewController2中的按鈕時,就會返回到咱們第一個ViewController1中了。

固然,在使用unwind segue方法時,也是會有一些回調幫助咱們進行跳轉前的設置和傳值,UIViewController以下方法會在跳轉前調用,返回NO,則不能進行跳轉:

-(BOOL)canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender{
    NSLog(@"canPerformUnwindSegueAction");
    return YES;
}

以後會執行咱們自定義的unwindSegue方法,這個方法中咱們能夠什麼都不寫,模式是會進行跳轉的。

二、使用代碼跳轉Storyboard中的controller

        咱們除了在Storyboard中拉拉扯扯能夠進行控制器的跳轉外,咱們也可使用代碼來跳轉Storyboard中segue鏈接關係。

        在Storyboard中兩個控制器間創建一個segue聯繫,咱們能夠取一個名字:

在觸發跳轉的方法中,使用以下方法進行跳轉,這裏面的參數id就是咱們取得segue的id:

- (void)performSegueWithIdentifier:(NSString *)identifier sender:(nullable id)sender NS_AVAILABLE_IOS(5_0);

下面三個屬性咱們能夠獲取controller的nib文件名,其storyBoard和其Bundle:

@property(nullable, nonatomic, readonly, copy) NSString *nibName;  
@property(nullable, nonatomic, readonly, strong) NSBundle *nibBundle; 
@property(nullable, nonatomic, readonly, strong) UIStoryboard *storyboard NS_AVAILABLE_IOS(5_0);

5、UIViewController之間的一些從屬關係

        這部分的內容和方法可能咱們接觸用到的並很少,可是在某些狀況下,使用這些方法能夠大大的方便某些邏輯。

一、parentViewController

        UIViewController裏面封裝了一個數組,能夠存放其子ViewController,系統中使用的例子就是導航和tabBar這類的控制器,咱們使用以下方法能夠直接訪問這些父的controller:

@property(nullable,nonatomic,weak,readonly) UIViewController *parentViewController;

二、模態跳轉中Controller的從屬

        在咱們進行控制器的跳轉時,只要控制器沒有被釋放,咱們均可以順藤摸瓜的找到它,使用以下兩個方法:

//其所present的contller,好比,A和B兩個controller,A跳轉到B,那麼A的presentedViewController就是B
@property(nullable, nonatomic,readonly) UIViewController *presentedViewController  NS_AVAILABLE_IOS(5_0);
//和上面的方法恰好相反,好比,A和B兩個controller,A跳轉到B,那麼B的presentingViewController就是A
@property(nullable, nonatomic,readonly) UIViewController *presentingViewController NS_AVAILABLE_IOS(5_0);

瞭解了上面方法咱們能夠知道,對於反向傳值這樣的問題,咱們根本不須要代理,block,通知等這樣的複雜手段,只須要獲取跳轉到它的Controller,直接設置便可。舉個例子,咱們須要在第二個界面消失後,改變第一個界面的顏色,在第二個controller中只須要下面的代碼便可實現 :

    self.presentingViewController.view.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    [self dismissViewControllerAnimated:YES completion:nil];

6、UIViewController的模態跳轉及動畫特效

        單純的UIViewController中,咱們使用最多的是以下的兩個方法,一個向前跳轉,一個向後返回:

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);
- (void)dismissViewControllerAnimated: (BOOL)flag completion: (void (^ __nullable)(void))completion NS_AVAILABLE_IOS(5_0);

從方法中,咱們能夠看到,有animated這個參數,來選擇是否有動畫特效,默認的動畫特效是像抽屜同樣從手機屏幕的下方向上彈起,固然,這個效果咱們能夠進行設置,UIViewController有以下一個屬性來設置動畫特效:

@property(nonatomic,assign) UIModalTransitionStyle modalTransitionStyle NS_AVAILABLE_IOS(3_0);

注意,這個要設置的是將要跳轉到的controller,枚舉以下:

typedef NS_ENUM(NSInteger, UIModalTransitionStyle) {
    UIModalTransitionStyleCoverVertical = 0,//默認的,從下向上覆蓋
    UIModalTransitionStyleFlipHorizontal ,//水平翻轉
    UIModalTransitionStyleCrossDissolve,//溶解
    UIModalTransitionStylePartialCurl ,從下向上翻頁
};

除了跳轉的效果,還有一個屬性能夠設置彈出的controler的填充效果,可是這個屬性只在pad上有效,在iphone上無效,都是填充到整個屏幕:

@property(nonatomic,assign) UIModalPresentationStyle modalPresentationStyle NS_AVAILABLE_IOS(3_2);
//枚舉以下
typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
        UIModalPresentationFullScreen = 0,//填充整個屏幕
        UIModalPresentationPageSheet,//留下狀態欄
        UIModalPresentationFormSheet,//四周留下變暗的空白
        UIModalPresentationCurrentContext ,//和跳轉到它的控制器保持一致
        UIModalPresentationCustom NS_ENUM_AVAILABLE_IOS(7_0),//自定義
        UIModalPresentationOverFullScreen NS_ENUM_AVAILABLE_IOS(8_0),
        UIModalPresentationOverCurrentContext NS_ENUM_AVAILABLE_IOS(8_0),
        UIModalPresentationPopover NS_ENUM_AVAILABLE_IOS(8_0) __TVOS_PROHIBITED,
        UIModalPresentationNone NS_ENUM_AVAILABLE_IOS(7_0) = -1,         
};

                       

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索