思考
- loadView汽纠、viewDidLoad卫键、viewDidAppear、initWithNibName虱朵、awakeFromNib等經(jīng)常出現(xiàn)在UIViewController中的方法介紹莉炉,這些方法分別用來做哪些工作钓账?換言之,創(chuàng)建自定義的view的代碼放到以上哪個方法中絮宁?
- 一個UIView的生命周期是怎樣的梆暮?以上幾個方法的調(diào)用順序如何?
- 通過IB和代碼加載視圖绍昂,有什么區(qū)別啦粹?
UIViewController
- 視圖層次和根視圖:每個視圖控制器都維護(hù)一個視圖層次(view hierarchy)。因為每個視圖都有自己的子視圖窘游,這個視圖層次其實(shí)也可以理解為一棵樹狀的數(shù)據(jù)結(jié)構(gòu)唠椭。而樹的根節(jié)點(diǎn),也就是根視圖(root view)忍饰,在UIViewController中以view屬性贪嫂,它可以被看做是其他所有子視圖的容器。
- 視圖加載方式:UIViewController采用懶加載的方式艾蓝,也就是說第一次訪問到view屬性時才會加載或創(chuàng)建它力崇。由于視圖由視圖控制器管理,所以討論視圖的加載方式時赢织,主要討論視圖控制器的加載方式亮靴。
- 通過Storyboard加載:這是蘋果推薦的方式,通過這種方式創(chuàng)建UIViewController對象的話敌厘,首先生成UIStoryboard類型的對象台猴,然后調(diào)用這個對象的 intantiateViewControllerWithIdentifier: 方法。
- 通過Nib文件加載:Nib文件其實(shí)就是xib文件俱两,Storyboard相當(dāng)于是聚合了多個nib文件饱狂,并且添加了對不同的UIViewController之間的segue和relationship的管理,但總的實(shí)現(xiàn)原理非常類似宪彩。通過這種方式加載視圖休讳,需要調(diào)用UIViewController類的 initWithNibName: bundle: 方法。
- 通過loadView方法加載:這就是通過代碼加載尿孔。這需要我們在loadView方法中俊柔,通過編程創(chuàng)建自己的視圖層次,并且把根視圖賦值給UIViewController的view屬性活合。因此通過代碼自定義view的時候雏婶,loadView方法大概是這樣的:
-(void)loadView
{
self.view=[[XXXView alloc] init];
}
處理視圖相關(guān)通知
-
當(dāng)視圖的可見性發(fā)生變化時,視圖控制器會自動調(diào)用一系列方法來響應(yīng)變化白指,所有可能的狀態(tài)留晚、方法和狀態(tài)之間的轉(zhuǎn)換關(guān)系在下圖中被明確標(biāo)出
可以看到每一個will方法都有自己對應(yīng)的did方法。但是如果我們在will方法中開始一個任務(wù)告嘲,不僅要在對應(yīng)的did方法中結(jié)束它错维,還要考慮到和這個will方法相反的那個will方法(注意到Appearing和Disappearing者兩個狀態(tài)是可以互相轉(zhuǎn)化的)
在運(yùn)行時展示View
- UIKit極大的簡化了加載和展示View的過程奖地,它大概會按照以下順序執(zhí)行一些任務(wù):
- 通過storyboard文件中的信息實(shí)例化視圖
- 連接outlet和action
- 調(diào)用UIViewController的awakeFromNib方法,要注意赋焕,在調(diào)用方法前的 traitcollection 為空且子視圖的位置可能不正確
- 把根視圖賦值給UIViewController的view屬性(其實(shí)就是調(diào)用loadView方法)
- 調(diào)用UIViewController的viewDidLoad方法
- 此時已經(jīng)完成了視圖的加載工作参歹,在展示到屏幕之前,還有以下幾個步驟:
- 調(diào)用UIViewController的viewWillAppear方法
- 更新視圖的布局
- 把視圖展示到屏幕上
- 調(diào)用UIViewController的viewDidAppear方法
- 至此隆判,第一個問題已經(jīng)幾乎解釋完了犬庇,還剩下一個awakeFromNib方法
- 我們已經(jīng)知道,awakeFromNib方法被調(diào)用時侨嘀,所有視圖的outlet和action已經(jīng)連接械筛,但還沒有確定。這個方法可以算作是和視圖控制器的實(shí)例化配合在一起使用的飒炎,因為有些需要根據(jù)用戶的喜好來進(jìn)行設(shè)置的內(nèi)容,無法存在storyboard中笆豁,所以可以在awakeFromNib方法中被加載進(jìn)來郎汪。
- awakeFromNib方法在視圖控制器的生命周期內(nèi)只會被調(diào)用一次。因為它和視圖控制器從nib文件中的解檔密切相關(guān)育特,和view的關(guān)系卻不大尖殃。
具體方法的解釋
- loadView 方法
- 當(dāng)執(zhí)行到loadView方法時偷溺,視圖控制器已經(jīng)從nib文件中被解檔并創(chuàng)建好了,接下來的任務(wù)主要是對view進(jìn)行初始化
- loadView方法在UIViewController對象的view屬性被訪問到且為空的時候調(diào)用照筑。
- 這是它與awakeFromNib方法的一個區(qū)別。假如我們在處理內(nèi)存警告時釋放view屬性(其實(shí)并不應(yīng)該這么做瘦陈,這里舉個例子):self.view = nil凝危。因此loadView方法在視圖控制器的生命周期內(nèi)可能會被多次調(diào)用。
- 這個方法不應(yīng)該被直接調(diào)用晨逝,而是有系統(tǒng)自動調(diào)用蛾默。它會加載或創(chuàng)建一個view并把它賦值給UIViewController的view屬性。
- 在創(chuàng)建view的過程中捉貌,首先會根據(jù)nibName去找對應(yīng)的Nib文件然后加載支鸡,如果nibName為空,或找不到對應(yīng)的Nib文件趁窃,則創(chuàng)建一個空視圖(這種情況一般是純代碼牧挣,也就是為什么說代碼構(gòu)建view的時候,要重寫loadView方法)醒陆。
- 注意在重寫loadView方法的時候瀑构,不要調(diào)用父類的方法。
- viewDidLoad 方法
- loadView方法執(zhí)行完之后统求,就會執(zhí)行viewDidLoad方法检碗。此時整個視圖層次(view hierarchy)已經(jīng)被放在內(nèi)存中据块。
- 無論是從nib文件加載,還是通過純代碼編寫界面折剃,viewDidLoad方法都會執(zhí)行另假,我們可以重寫這個方法,對通過nib文件加載的view做一些其他的初始化工作怕犁。比如可以移除一些視圖边篮,修改約束,加載數(shù)據(jù)等
- viewWillAppear和viewDidAppear方法
- 在視圖加載完成奏甫,并即將顯示在屏幕上時戈轿,會調(diào)用viewWillAppear方法,在這個方法里阵子,可以改變當(dāng)前屏幕的方向或狀態(tài)欄的風(fēng)格等思杯。
- 當(dāng)viewWillAppear方法執(zhí)行完后,系統(tǒng)會執(zhí)行viewDidAppear方法挠进。在這個方法中色乾,還可以對視圖做一些關(guān)于展示效果方面的修改。
視圖的生命周期
到目前為止领突,我們已經(jīng)了解了每個方法的作用暖璧,接下來就把整個流程梳理一遍。
- -[ViewController initWithCoder:] 或 -[ViewController initWithNibName: Bundle: ] 首先從歸檔文件中加載UIViewController對象君旦。即使是純代碼澎办,也會把nil作為參數(shù)傳給后者。
- -[UIView awakeFromNib:] 作為第一個方法的助手金砍,方便處理一些額外的設(shè)置局蚀。
- -[ViewController loadView] 創(chuàng)建或加載一個view并把它賦值給UIViewController的view屬性
- -[ViewController viewDidLoad] 此時整個視圖層次(view hierarchy)已經(jīng)被放到內(nèi)存中,可以移除一些視圖恕稠,修改約束至会,加載數(shù)據(jù)等
- -[ViewController viewWillAppear:] 視圖加載完成,并即將顯示在屏幕上谱俭,還沒有設(shè)置動畫奉件,可以改變當(dāng)前屏幕方向和狀態(tài)欄的風(fēng)格等
- -[ViewController viewWillLayoutSubviews] 即將開始子視圖位置布局
- -[ViewController viewDidLayoutSubviews] 用于通知視圖的位置布局已經(jīng)完成
- -[ViewController viewDidAppear:] 視圖已經(jīng)展示在屏幕上,可以對視圖做一些關(guān)于展示效果方面的修改
- -[ViewController viewWillDisAppear:] 視圖即將消失
- -[ViewController viewDidDisAppear:] 視圖已經(jīng)消失
如果考慮UIViewController可能在某個時刻釋放整個view昆著。那么再次加載視圖時顯然會從步驟3開始县貌。因為此時的UIViewController對象依然存在。
總結(jié)
- 只有init系列的方法凑懂,如initWithNibName需要自己調(diào)用煤痕,其他方法如loadView和awakeFromNib則是系統(tǒng)自動調(diào)用。而viewWill/Did系列的方法則類似于回調(diào)和通知,也會自動調(diào)用摆碉。
- 純代碼寫視圖布局時需要注意塘匣,要手動調(diào)用loadView方法,而且不要調(diào)用父類的loadView方法巷帝。純代碼和用IB的區(qū)別僅存在于loadView方法及其之前忌卤,編程時需要注意的也就是loadView方法。
- 除了initWithNibName和awakeFromNib方法是處理視圖控制器外楞泼,其他方法都是處理視圖驰徊。這兩個方法在視圖控制器的生命周期里只會調(diào)用一次。