简介
总结一下视图的生命周期原理,其实在开发中我们只需要知道怎么用就足以应付日常的任务了,了解一些原理可以帮助我们更灵活的应用。
目录
1、视图周期
2、loadView的设计目的
3、loadView的作用
4、控制器加载视图的流程
5、实际应用的案例
视图的生命周期验证
1、打印View的生成方法
- (void)awakeFromNib {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
- (void)loadView {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
//视图加载完成
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
// 视图将要显示
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
// 视图将要布局子视图
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
//视图完成布局子视图
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
/// 视图完成显示
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
// 视图将要消失
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
//视图已经消失
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
NSLog(@"%@ %s", [self class], __FUNCTION__);
// 判断当前视图是否已经被加载
if (self.isViewLoaded && self.view.window == nil) {
// 释放根视图
self.view = nil;
}
}
运行打印的结果:
可以看到先走了awakeFromNib,awakeFromNib是什么,顾名思义,系统从Nib加载,Nib也就是Xib,先去加载Xib的视图,然后执行loadView,然后经历了视图加载,视图即将出现,添加用户自定义的子视图,视图即将消失,已经消失这几个阶段。
视图出现之后就是我们眼睛可以看得见的东西,我们可以注意到,在viewDidLoad之前,有一个loadView,这个方法是做什么呢?
我们来验证一下:
说明:(OneViewController 关联了storyboard)
一、重新这个方法,里边使用self.view
- (void)loadView {
self.view.backgroundColor = [UIColor redColor];
NSLog(@"%@ %s", [self class], __FUNCTION__);
//
}
运行发现,程序崩溃了,这是为什么???并且self.view = nil;
看崩溃断点:
发现控制器的父类在调用view这个方法之后,子类OneViewController调用loadView这个方法,如此循环。。。为什么父类创建一个view会触发子类的loadView的方法,子类又去触发父类的方法?
再来验证二:
在这个方法里不去调用self.view
- (void)loadView {
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
运行发现,加载了storyboard 的视图出来,为什么此时不崩溃?self.view这句代码是出了什么问题?
验证三:
- (void)loadView {
self.view = [[UIView alloc] init];
self.view.backgroundColor = [UIColor redColor];
NSLog(@"%@ %s", [self class], __FUNCTION__);
//
}
运行也不崩溃,但是视图是红色的。
验证四:
- (void)loadView {
self.view = [[UIView alloc] init];
self.view.backgroundColor = [UIColor redColor];
[super loadView];
NSLog(@"%@ %s", [self class], __FUNCTION__);
//
}
运行结果是加载了storyboard 的视图出来。
根据上边几个验证,我们发现,验证一中,会死循环,是因为在loadView方法里使用到控制器的根视图view的时候,当前view是空的,当系统发现view根本不存在,内存没有它的地址的时候就会尝试去调用loadView的方法,自然就触发了子控制器的loadView, 然后在子控制器又使用了self.view,所以会形成一个死循环。通过验证三我们知道,初始化一个UIView并且赋值给self.view就会不会崩溃,到这样我们已经可以看到loadView的作用就是初始化一个view。
结论:loadView的设计目的就是创建视图控制器的根视图,是开发者创建一个控制器所有UI的基础
归纳以下几点:
1、加载一个控制器之前,系统会先在loadView方法中判断是否已经有一个View可以作为根视图
2、如果根视图被使用的时候,并且是nil的,就会尝试去创建一个view并赋值给控制器的根视图
3、原则上,你不需要去调用loadView方法,因为系统自动调用了
4、如果在代码中,你重写了loadView,一旦调用 super,会执行系统默认的加载 stroyboard 或者 xib 的工作,loadView内super之前的代码全部白写,也就是,只要你不调用super,系统就会从你自定义的View中加载出来,这时候与 stroyboard 或者 xib 无关。
问:我们已经知道,loadView的出现就是为了创建一个根视图,并且系统自动帮助我们调用了,那么,它的出现对于开发者有什么意义呢?
答:它出现的意义就是可以让开发者可以自定义根视图。例如:使用loadView方法触发nib中UIView的加载。
我们用一个流程图来总结:
创建一个根视图的顺序:
1、是否有一个view可用
2、如果有,直接显示这个view;如果没有,则系统自动调用loadView方法去生成一个view
3、当重写了loadView方法,并在里边自定义了view则直接使用这个view作为根视图
4、如果调用了[super loadView]; ,系统就优先去加载stroyboard或者xib的视图
loadView的实际应用
我们先看看一个实际开发中的问题,加载一个Xib控制器。
/// 视图加载完成
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *clickBtn = [[UIButton alloc]initWithFrame: CGRectMake(100, 200,[UIScreen mainScreen].bounds.size.width - 200, 50)];
[clickBtn setTitle:@"跳转到Xib控制器" forState:UIControlStateNormal];
[self.view addSubview:clickBtn];
[clickBtn addTarget:self action:@selector(clickBtnClick) forControlEvents:UIControlEventTouchUpInside];
NSLog(@"%@ %s", [self class], __FUNCTION__);
}
- (void)clickBtnClick{
XibViewController *vc = [[XibViewController alloc]initWithNibName:@"XibViewController" bundle:nil];
// [vc loadView];
vc.beautifulImageView.image = [UIImage imageNamed:@"img2"];
vc.titleLab.text = @"我想看美女";
[self presentViewController:vc animated:YES completion:nil];
}
运行后点击按钮跳转到Xib控制器,发现即使我在跳转之前重新设置了图片和文字,但是加载的还是默认的,那怎么办?
上边已经论证了,loadView有一个方法就是用来加载控制器的根视图的,修改之后出现默认的图片和文字是因为控制器push出来之后,根视图也会加载出来,但是系统默认会去Xib文件中寻找是否有可用的view,发现有一个默认的view,直接加载出来,即使你修改也没用。
解决办法:重写loadview的方法,让系统去使用我们自定义的视图view
- (void)clickBtnClick{
XibViewController *vc = [[XibViewController alloc]initWithNibName:@"XibViewController" bundle:nil];
[vc loadView];
vc.beautifulImageView.image = [UIImage imageNamed:@"img2"];
vc.titleLab.text = @"我想看美女";
[self presentViewController:vc animated:YES completion:nil];
}
运行
大功告成!