iOS控制器加载底层原理和UI初始化的过程

控制器加载底层原理

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文件屏幕尺寸设置在运行时的影响

    1. xib文件中选择7plus屏幕大小的尺寸414 x 716
    1. 选择7plus模拟器加载,在viewDidLoad打上断点po一下,结果view的frame = (0, 0, 320, 480);
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor blueColor];
    //断点处,po self.view
}
    1. 我们可以得到结论在viewDidLoad中,self.view并没有成型。
    1. 所以我们在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 stackcontext
  • 处理结束后移除此上下文
  • drawRect配合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 

IB_DESIGNABLE

@interface CustomView : UIView

@end
  • 可视化参数
#import 

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属性的设置选项


    iOS控制器加载底层原理和UI初始化的过程_第1张图片
    strokeColor.png

你可能感兴趣的:(iOS控制器加载底层原理和UI初始化的过程)