當你訪問一個ViewController的view屬性時,如果此時view的值是nil,那麼,ViewController就會自動調用loadView這個方法。這個方法就會加載或者創建一個view對象,賦值給view屬性。
loadView默認做的事情是:如果此ViewController存在一個對應的nib文件,那麼就加載這個nib。否則,就創建一個UIView對象。
如果你用Interface Builder來創建界面,那麼不應該重載這個方法。
控制器的loadView方法以及view屬性
控制器有一個view屬性,我們經常在控制器中通過self.view來訪問。這個view是一個很有意思的東西。
首先要明白這個view到底是什麼東西。
一個控制器成為window的根控制器的時候,當這個界面即將顯示,控制器的view會被加到window中用來顯示界面。正常情況下控制器自帶的view是"無色透明"的。
如果你在設置window的根控制器前創建一個Button直接加到window上,此時button出現在window上,但是在控制器的view的下面。此時點擊按鈕會發現按鈕並不會響應點擊。
這是因為控制器的view攔截了點擊事件,這說明它並非真的完全透明(因為如果完全透明,那麼不會攔截點擊事件,可以設置viewController.view.alpha = 0來驗證,此時按鈕就可以響應點擊)。事實上view是一個backgroundColor為clearColor的視圖,clearColor是幾乎透明的顏色但是並非完全透明,也就是說當有類似點擊事件發生的時候,clearColor背景的view會攔截點擊事件,因為它並非完全透明。
所以可以說控制器的view是一個看不見摸得著的東西...
另外view是懶加載的,也就是說,只有真正使用到控制器的view的時候它才會被創建出來。它就是在控制器的-loadView方法中創建出來的。
復制代碼 代碼如下:
- (void)loadView
{
// 類似這種實現
// 可能還會進行更多的操作,比如判斷是否有指定storyboard,如果是就會加載storyboard中控制器的view等操作
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor clearColor];
// ...
_view = view;
}
前面說過,view是懶加載的,所以當使用self.view的時候:
復制代碼 代碼如下:
- (UIView *)view
{
// 類似這種實現
if (_view == nil) {
[self loadView];
[self viewDidLoad];
}
return _view;
}
當_view為nil的時候會調用loadView方法方法,此時系統默認會創建一個UIView對象並將其賦值給_view,此時_view有了值,接著調用viewDidLoad方法。
當再次訪問self.view的時候,因為_view已經有值,所以會直接返回_view。這也是為什麼loadView以及viewDidLoad方法只會執行一次。
之前的文章提到過,在給window設置完根控制器,根控制器的view並不是馬上就被加到window上,也就是說此時view還未創建。在[self.window makeKeyAndVisible]之後界面即將顯示的時候,這時候需要用到view,此時就會調用view的getter方法,接著執行loadView,在接著執行viewDidLoad等一系列方法。
現在我們知道view是懶加載的,所以如果在[self.window makeKeyAndVisible]之前就用到控制器的view,那麼它就會"提前”創建。
復制代碼 代碼如下:
NSLog(@"%@", viewController.view); // 此時用到view,就會創建view
現在我們知道控制器的view是通過loadView方法創建的了,所以我們可以覆寫這個方法來達到改變控制器的自帶view的目的:
復制代碼 代碼如下:
- (void)loadView
{
JYView *view = [[JYView alloc] initWithFrame:[UIScreen mainScreen].bounds];
view.background = [UIColor blueColor];
_view = view;
}
此時我們就改變了控制器原本自動創建的view,現在使用self.view則得到的是JYView的對象。
loadView使用中的一些注意點
永遠不要主動調用這個函數。view controller會在view的property被請求並且當前view值為nil時調用這個函數。如果你手動創建view,你應該重載這個函數。如果你用IB創建view並初始化view controller,那就意味著你使用initWithNibName:bundle:方法,這時,你不應該重載loadView函數。
這個方法的默認實現是這樣:先尋找有關可用的nib文件的信息,根據這個信息來加載nib文件,如果沒有有關nib文件的信息,默認實現會創建一個空白的UIView對象,然後讓這個對象成為controller的主view。
所以,重載這個函數時,你也應該這麼做。並把子類的view賦給view屬性(property)(你create的view必須是唯一的實例,並且不被其他任何controller共享),而且你重載的這個函數不應該調用super。
如果你要進行進一步初始化你的views,你應該在viewDidLoad函數中去做。在iOS 3.0以及更高版本中,你應該重載viewDidUnload函數來釋放任何對view的引用或者它裡面的內容(子view等等)。