iOS UI

iOS 自动布局触发时机,相关方法调用顺序

2020-08-30  本文已影响0人  你duck不必呀
配图.jpg

正常情况写布局的两种方式

一、手写布局

就是直接设置view的frame属性,不详细说明了

二、自动布局

自动布局Auto Layout,苹果为我们提供了一整套布局系统(layout Engine),这套系统会将视图、约束、优先级、大小通过计算转换成对应的frame,而且当约束改变的时候,会再次触发该系统重新计算。整个过程如下图:(图片出自WWDC2015 地址)

image.png

激活,失活
创建约束,优先级
添加,移除视图

检测到改变后系统(Layout Engine) 会重新计算出布局,就会调用superview.setNeedsLayout()

容错处理
从上往下调用layoutSubviews()
从 Layout Engine 拷贝出子视图 frame

到此,系统就计算好frame了,接下来就是渲染了,和手写布局是一样的

Layout Engine 通过系统Run Loop 循环cycle

整个流程可以理解为,当修改约束的时候,会触发Layout Engine去计算出view的frame,然后从上而下布局,最后在下一个运行循环中更新界面

整个布局流程可以分为三个阶段:

计算frame,布局,渲染

1.计算阶段

- (void)updateConstraints;

用xib或者NSLayoutConstraint自动布局都会调用该方法,是通过手布局(创建view,设置frame)不会调用此方法

补充:基于约束的布局是懒加载触发的,所以只有设置了约束系统才会调用updateConstraints,如果把基于frame的布局写到updateConstraints,系统是不知道你的布局方式,通过重写下面这个方法,返回YES,系统就会调用updateConstraints

+ (BOOL)requiresConstraintBasedLayout{
    return YES;
}

布局可以写在updateConstraints中,并且必须调用 [super updateViewConstraints]
苹果不建议把初始化的约束写在这个方法里,原因如下:
(1) 当视图约束被更新的时候(一般是被setNeedsUpdateConstraints标记更新) updateConstraints这个方法会被调用,如果里面包含大量的约束,系统就需要去判断是否已经存在相同的约束,
(2) 当前view不一定拥有所有的约束,其他view可能已经向该view添加了部分约束
(3) 如果在点击事件中触发修改约束的行为,修改布局的代码和触发更新的代码不再同一处,这会让逻辑变得难以遵循

仅需要更新约束的这部分代码写到updateViewConstraints,大量的初始化约束写到类似于 init,viewDidLoad中.

- (void)updateViewConstraints{
    [self.subView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view).offset(100);
        make.left.equalTo(self.view).offset(0);
        make.right.equalTo(self.view).offset(-100);
        make.bottom.equalTo(self.view).offset(-100);
    }];
    [super updateViewConstraints];
}

此外还会通过以下方式触发系统调用: updateConstraints

[self.subView setNeedsUpdateConstraints];
ViewDemo[19068:895608] -[subView updateConstraints]
[self.subView updateConstraintsIfNeeded];

2.布局阶段

- (void)layoutSubviews;

此方法由系统调用,被调用时,系统已经计算好view对应的frame,如果需要修改布局,通过重写这个方法,并在方法体里修改frame,但是在方法里需要注意
(1) 在方法里必须调用 super.layoutSubviews()
(2) 不能在方法里修改约束,修改约束会触发系统重新计算布局,可能会导致布局错乱
(3) 不能在方法里调用setNeedsUpdateConstraints(),setNeedsLayout如,可能会导致布局错乱

除此之外,还会通过以下方式触发:

UIView *subView = [[UIView alloc] init];
[self.view addSubview:subView];
[self.subView setNeedsLayout];
 [self.subView layoutIfNeeded];

3.渲染阶段

- (void)drawRect:(CGRect)rect;

当视图在屏幕上出现的时候 -drawRect:方法就会被自动调用。-drawRect:方法里面的代码利用Core Graphics去绘制一个寄宿图,然后内容就会被缓存起来直到它需要被更新

- (void)setNeedsDisplay,标记需要显示,在下个drawing cycle 调用

在UIViewController中

- (void)updateViewConstraints;

控制器的view.updateConstraints()方法调用时,对应控制器的updateViewConstraints就会被调用,控制器view的子view约束改变是不会触发的.

- (void)viewWillLayoutSubviews

当视图控制器的视图的边界发生变化时,该视图将调整其子视图的位置,然后系统调用此方法。但是,调用此方法并不表示该视图的子视图的各个布局已调整。每个子视图负责调整其自己的布局。
视图布局子视图后,视图控制器可以重写此方法以进行更改。此方法的默认实现不执行任何操作。

- (void)viewDidLayoutSubviews

当视图控制器的视图的边界发生变化时,视图将调整其子视图的位置,然后系统调用此方法。但是,调用此方法并不表示该视图的子视图的各个布局已调整。每个子视图负责调整其自己的布局。
视图布局子视图后,视图控制器可以重写此方法以进行更改。此方法的默认实现不执行任何操作。

三个方法在控制器view的子view约束改变时是不会触发的.

在viewController中这三个方法是系统为我们提供的便利,方便我们在控制器自带的view发生变化的时候做相应的操作:

updateViewConstraints->viewWillLayoutSubviews->viewDidLayoutSubviews

总结一下:

在同一代码处即有自动布局又有手动布局

获取,修改view.frame

translatesAutoresizingMaskIntoConstraints

在xib中,如果设置了auto layout 该属性默认是NO
在手写代码中,该属性默认是YES

cafe-3537801_1280.jpg
上一篇下一篇

猜你喜欢

热点阅读