控制器有一个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的对象
。