iOS控制器加载底层原理和UI初始化的过程
2017-08-28 本文已影响62人
找不到工作的iOS
控制器加载底层原理
1.有xib文件的控制器加载
- 1. [init]
- 2. [initWithNibName: bundle:]
- 3. [viewDidLoad]
- 4. [viewWillAppear:]
- 5. [viewDidAppear:]
- 可以看出
init
里面封装了initWithNibName
- 如果
bundle的
参数为nil
,则默认为[NSBundle mainBundle]
- xib编译后会变为nib文件(包内容-资源文件)
2.无xib文件的控制器加载
- 1. [loadView] //有xib获取xib的view,没有自己生成
- 2. [viewDidLoad]
- 3. [viewWillAppear]
- 4. [viewWillLayoutSubviews]
- 5. [viewDidLayoutSubviews:]
- 6. [viewDidAppear:]
- 如果没有xib文件,就会通过
loadView
方法生成一个self.view
-
[loadView]
(苹果建议不要直接调用此参数,其实可以无视) - 如果在
[loadView]
里面没有调用[super loadView];
会进入一个死循环,实质是获取不到一个UIView
的实例。 -
self.view
实质是一个懒加载,伪代码实现如下
- (UIView *)view {
if(!_view) {
[self loadView];
//如果这里没有对self.view初始化,就会一直循环
[self viewDidLoad];
}
return _view;
}
3.实战应用 - 拆分Controller
-
Controller
主要是起到一些调度作用 - 我们可以将一些业务逻辑转移到
CustomView
,可以采用block
KVO
结合实现 -
MVCS
设计模式就是基于loadView
实现
- (void)loadView {
//[super loadView]; //系统的
self.view = [CustomView new]; //自己的 (给控制器自己定义的)
}
4.探究xib文件屏幕尺寸设置在运行时的影响
-
xib
文件中选择7plus
屏幕大小的尺寸414 x 716
-
- 选择
7plus
模拟器加载,在viewDidLoad
打上断点po
一下,结果view的frame = (0, 0, 320, 480);
- 选择
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
//断点处,po self.view
}
- 我们可以得到结论在
viewDidLoad
中,self.view
并没有成型。
- 我们可以得到结论在
- 所以我们在
viewDidAppear
或者viewWillLayout
操作布局会比viewDidLoad
安全
- 所以我们在
UI初始化的过程
1.无xib的UIView加载
1.[init]
2.[initWithFrame:]
3.[layoutSubview]
2.有xib的UIView加载
1.[initWithCoder]
2.[awakeFromNib:]
3.[layoutSubview] //只要addSubView都会执行
3.运行时的布局成型时机之[layoutSubview]
- 如果在
initWithFrame
做布局或者添加子视图的操作,可能会无法达到预期的效果,比如UIButton
UILabel
- 原因是在
initWithFrame
中视图并没有完全成型,最安全的做法实在layoutSubview
中做布局操作 - 子视图的初始化操作还是可以放在父视图的
initWithFrame
中的 - 当我们在外部需要调用
[layoutSubview]
的时候,一般不用[self layoutSubview]
而用[self setNeedsLayout]
-[setDisplay]
类似[layoutSubview]
,他调用的是[drawRect]
- 多次调用
[self setNeedsLayout]
只会在当前runloop
执行一次[layoutSubview]
-
[layoutIfNeeded]
立刻刷新布局,获取准确的frame
。可以结合[self setNeedsLayout]
使用,立刻执行完[layoutSubview]
的布局配置
4.[layoutSubview]
实战运用
- 如果你已经明白3.中我表达的内容,那么我们在对
button
自带的title
以及imageview
的大小与位置不满意的时候,可以在自定义的button
内的layoutSubview
重写frame
#import "CustomButton.h"
@implementation CustomButton
- (void)layoutSubviews {
self.titleLabel.frame = CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>);
self.imageView.frame = CGRectMake(<#CGFloat x#>, <#CGFloat y#>, <#CGFloat width#>, <#CGFloat height#>);
}
@end
-[tableView reload]
由于具有一定的延迟,如果我们要立刻获取某个cell
的准确布局位置,那么可以
[tableView reload];
[self setNeedsLayout];
[self layoutIfNeeded];
5.[darwRect:]
- 当系统主动调用到
[darwRect:]
的时候,首先内部会生成当前layer
的上下文 - 然后将这个上下文
push
到栈顶,系统默认处理top of stack
的context
- 处理结束后移除此上下文
-
drawRec
t配合xib
可以让控制可视化调整,注意的是只是修改的是layer
层,比如添加一个subview
是不会可视化的
- (void)drawRect:(CGRect)rect {
//系统底层的执行思想
CGContextRef contextRef; //获取当前CALayer的上下文,实际不能这样操作,只是一个示意
UIGraphicsPushContext(contextRef); //push到top of stack
UIGraphicsPopContext(); //从top of stack移除,并且把current context恢复为上一个context
}
6.视图可视化操作
- 可视化属性 IB_DESIGNABLE
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface CustomView : UIView
@end
- 可视化参数
#import <UIKit/UIKit.h>
IB_DESIGNABLE
@interface CustomView : UIView
@property (nonatomic,strong)IBInspectable UIColor *strokeColor;
@end
#import "CustomView.h"
@implementation CustomView
- (void)drawRect:(CGRect)rect {
[self.strokeColor setStroke];
}
@end
-
此时xib中就有了strokeColor属性的设置选项
strokeColor.png