序言:
loadView、viewDidLoad膊升、viewDidAppear示血、initWithNibName、awakeFromNib等經(jīng)常出現(xiàn)在UIViewController中的方法介紹桃笙。
這些方法分別用來(lái)作哪些工作,換言之沙绝,創(chuàng)建自定義的View時(shí)代碼放到以上哪個(gè)方法中搏明。
一個(gè)UIView的生命周期是怎樣的。以上幾個(gè)方法的調(diào)用順序如何闪檬。
通過(guò)IB和代碼加載視圖星著,有什么區(qū)別
UIViewController
視圖層次和根視圖
每個(gè)視圖控制器都維護(hù)一個(gè)視圖層次(view hierarchy)。
因?yàn)槊總€(gè)視圖都有自己的子視圖粗悯,這個(gè)視圖層次其實(shí)也可以理解為一棵樹(shù)狀的數(shù)據(jù)結(jié)構(gòu)虚循。而樹(shù)的根節(jié)點(diǎn),也就是根視圖(root view)样傍,在UIViewController中以view屬性邮丰。它可以被看做是其他所有子視圖的容器。
視圖的加載方式
UIViewController采用懶加載的方式铭乾,也就是說(shuō)第一次訪(fǎng)問(wèn)到view屬性時(shí)才會(huì)加載或創(chuàng)建它。由于視圖由視圖控制器管理娃循,所以討論視圖的加載方式時(shí)炕檩,主要討論視圖控制器的加載方式。
通過(guò)Storyboard加載:這是蘋(píng)果推薦的方式捌斧,也是未來(lái)的趨勢(shì)笛质。
通過(guò)這種方式創(chuàng)建UIViewController對(duì)象的話(huà),首先生成UIStoryboard類(lèi)型的對(duì)象捞蚂,然后調(diào)用這個(gè)對(duì)象的instantiateViewControllerWithIdentifier:方法
通過(guò)Nib文件加載:
Nib文件其實(shí)就是xib文件妇押,Storyboard相當(dāng)于是聚合了多個(gè)nib文件,并且添加了對(duì)不同的UIViewController之間的segue和relationship的管理姓迅。但總的實(shí)現(xiàn)原理非常類(lèi)似
通過(guò)這種方式加載視圖,需要調(diào)用UIViewController類(lèi)的initWithNibName:bundle:方法
通過(guò)loadview方法加載:
這就是通過(guò)代碼加載敲霍。這需要我們?cè)趌oadView 方法中俊马,通過(guò)編程創(chuàng)建自己的視圖層次,并且把把根視圖賦值給UIViewController的view屬性肩杈。
因此柴我,通過(guò)代碼自定義View的時(shí)候,loadView 方法大概是這樣的:
- (void)loadView{
self.view = [[XXXView alloc] init];
}
處理視圖相關(guān)通知
當(dāng)視圖的可見(jiàn)性發(fā)生變化時(shí)扩然,視圖控制器會(huì)自動(dòng)調(diào)用一系列方法來(lái)響應(yīng)變化艘儒。
所有可能的狀態(tài)、方法和狀態(tài)之間的轉(zhuǎn)換關(guān)系在下圖中被明確標(biāo)出夫偶。
可以看到每一個(gè)will方法都有自己對(duì)應(yīng)的did方法界睁。但是如果我們?cè)趙ill方法中開(kāi)始一個(gè)任務(wù),不僅要在對(duì)應(yīng)的did方法中結(jié)束它兵拢,還要考慮到和這個(gè)will方法相反的那個(gè)will方法(注意到Appearing和Disappearing這兩個(gè)狀態(tài)是可以互相轉(zhuǎn)化的)
在運(yùn)行時(shí)展示View
UIKit極大的簡(jiǎn)化了加載和展示View的過(guò)程翻斟,它大概會(huì)按照以下順序執(zhí)行一些任務(wù):
通過(guò)storyboard文件中的信息實(shí)例化視圖
連接outlet和action
把根視圖賦值給UIViewController的view屬性(其實(shí)就是調(diào)用loadView 方法)
調(diào)用UIViewController的awakeFromNib方法。要注意卵佛,在調(diào)用方法前杨赤,的trait collecion為空且子視圖的位置可能不正確
調(diào)用UIViewController的viewDidLoad方法。
此時(shí)已經(jīng)完成了視圖的加載工作截汪,在展示到屏幕之前疾牲,還有以下幾個(gè)步驟:
調(diào)用UIViewController的viewWillAppear方法。
更新視圖的布局
把視圖展示到屏幕上
調(diào)用UIViewController的viewDidAppear方法衙解。
awakeFromNib方法
至此阳柔,第一個(gè)問(wèn)題已經(jīng)幾乎解釋完了,還剩一個(gè)awakeFromNib方法蚓峦。
我們已經(jīng)知道舌剂,awakeFromNib方法被調(diào)用時(shí),所有視圖的outlet和action已經(jīng)連接暑椰,但還沒(méi)有被確定霍转。這個(gè)方法可以算作是和視圖控制器的實(shí)例化配合在一起使用的,因?yàn)橛行┬枰鶕?jù)用戶(hù)喜好來(lái)進(jìn)行設(shè)置的內(nèi)容一汽,無(wú)法存在storyboard中避消,所以可以在awakeFromNib方法中被加載進(jìn)來(lái)。
awakeFromNib方法在視圖控制器的生命周期內(nèi)只會(huì)被調(diào)用一次召夹。因?yàn)樗鸵晥D控制器從nib文件中的解檔密切相關(guān)岩喷,和view的關(guān)系卻不大。
具體方法的解釋
loadView方法
當(dāng)執(zhí)行到loadView方法時(shí)监憎,視圖控制器已經(jīng)從nib文件中被解檔并創(chuàng)建好了纱意,接下來(lái)的任務(wù)主要是對(duì)view進(jìn)行初始化。
loadView方法在UIViewController對(duì)象的view屬性被訪(fǎng)問(wèn)到且為空的時(shí)候調(diào)用鲸阔。
這是它與awakeFromNib方法的一個(gè)區(qū)別偷霉。假設(shè)我們?cè)谔幚韮?nèi)存警告時(shí)釋放view屬性(其實(shí)并不應(yīng)該這么做迄委,這里舉個(gè)例子):self.view = nil。因此loadView方法在視圖控制器的生命周期內(nèi)可能會(huì)被多次調(diào)用腾它。
這個(gè)方法不應(yīng)該被直接調(diào)用跑筝,而是由系統(tǒng)自動(dòng)調(diào)用。它會(huì)加載或創(chuàng)建一個(gè)view并把它賦值給UIViewController的view屬性瞒滴。
在創(chuàng)建view的過(guò)程中曲梗,首先會(huì)根據(jù)nibName去找對(duì)應(yīng)的Nib文件然后加載。如果nibName為空妓忍,或找不到對(duì)應(yīng)的Nib文件虏两,則會(huì)創(chuàng)建一個(gè)空視圖(這種情況一般是純代碼,也就是為什么說(shuō)代碼構(gòu)建View的時(shí)候世剖,要重寫(xiě)loadView 方法)定罢。
注意在重寫(xiě)loadView方法的時(shí)候,不要調(diào)用父類(lèi)的方法旁瘫。
viewDidLoad方法
loadView方法執(zhí)行完之后祖凫,就會(huì)執(zhí)行viewDidLoad方法。此時(shí)整個(gè)視圖層次(view hierarchy)已經(jīng)被放到內(nèi)存中酬凳。
無(wú)論是從nib文件加載惠况,還是通過(guò)純代碼編寫(xiě)界面,viewDidLoad方法都會(huì)執(zhí)行宁仔。我們可以重寫(xiě)這個(gè)方法稠屠,對(duì)通過(guò)nib文件加載的view做一些其他的初始化工作。比如可以移除一些視圖翎苫,修改約束权埠,加載數(shù)據(jù)等。
viewWillAppear和viewDidAppear方法
在視圖加載完成煎谍,并即將顯示在屏幕上時(shí)攘蔽,會(huì)調(diào)用viewWillAppear方法,在這個(gè)方法里呐粘,可以改變當(dāng)前屏幕方向或狀態(tài)欄的風(fēng)格等秩彤。
當(dāng)viewWillAppear方法執(zhí)行完后,系統(tǒng)會(huì)執(zhí)行viewDidAppear方法事哭。在這個(gè)方法中,還可以對(duì)視圖做一些關(guān)于展示效果方面的修改瓜富。
視圖的生命歷程
到目前為止鳍咱,我們已經(jīng)了解了每個(gè)方法的作用,接下來(lái)就把整個(gè)流程梳理一遍与柑。
[objc] view plain copy
-[ViewController initWithCoder:]或-[ViewController initWithNibName:Bundle]:首先從歸檔文件中加載UIViewController對(duì)象谤辜。即使是純代碼蓄坏,也會(huì)把nil作為參數(shù)傳給后者。
-[UIView awakeFromNib]:作為第一個(gè)方法的助手丑念,方便處理一些額外的設(shè)置涡戳。
-[ViewController loadView]:創(chuàng)建或加載一個(gè)view并把它賦值給UIViewController的view屬性
-[ViewController viewDidLoad]:此時(shí)整個(gè)視圖層次(view hierarchy)已經(jīng)被放到內(nèi)存中,可以移除一些視圖脯倚,修改約束渔彰,加載數(shù)據(jù)等
-[ViewController viewWillAppear:]:視圖加載完成,并即將顯示在屏幕上,還沒(méi)有設(shè)置動(dòng)畫(huà)推正,可以改變當(dāng)前屏幕方向或狀態(tài)欄的風(fēng)格等恍涂。
-[ViewController viewWillLayoutSubviews]:即將開(kāi)始子視圖位置布局
-[ViewController viewDidLayoutSubviews]:用于通知視圖的位置布局已經(jīng)完成
-[ViewController viewDidAppear:]:視圖已經(jīng)展示在屏幕上,可以對(duì)視圖做一些關(guān)于展示效果方面的修改植榕。
-[ViewController viewWillDisappear:]:視圖即將消失
-[ViewController viewDidDisappear:]:視圖已經(jīng)消失
如果考慮UIViewController可能在某個(gè)時(shí)刻釋放整個(gè)view再沧。那么再次加載視圖時(shí)顯然會(huì)從步驟3開(kāi)始。因?yàn)榇藭r(shí)的UIViewController對(duì)象依然存在尊残。
總結(jié)
1炒瘸,只有init系列的方法,如initWithNibName需要自己調(diào)用,其他方法如loadView和awakeFromNib則是系統(tǒng)自動(dòng)調(diào)用寝衫。而viewWill/Did系列的方法則類(lèi)似于回調(diào)和通知顷扩,也會(huì)被自動(dòng)調(diào)用。
2竞端,純代碼寫(xiě)視圖布局時(shí)需要注意屎即,要手動(dòng)調(diào)用loadView方法,而且不要調(diào)用父類(lèi)的loadView方法事富。純代碼和用IB的區(qū)別僅存在于loadView方法及其之前技俐,編程時(shí)需要注意的也就是loadView方法。
3统台,除了initWithNibName和awakeFromNib方法是處理視圖控制器外雕擂,其他方法都是處理視圖。這兩個(gè)方法在視圖控制器的生命周期里只會(huì)調(diào)用一次贱勃。