美文网首页iOS UI
iOS 自动布局触发时机,相关方法调用顺序

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

作者: 你duck不必呀 | 来源:发表于2020-08-30 17:43 被阅读0次
    配图.jpg

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

    一、手写布局

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

    二、自动布局

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

    image.png
    • Constraints Change

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

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

    • Deferred Layout Pass

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

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

    • Application Run Loop

    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

    • setNeedsUpdateConstraints 标记更新约束,会在下次 Cycle中自动调用updateConstraints,
    [self.subView setNeedsUpdateConstraints];
    
    ViewDemo[19068:895608] -[subView updateConstraints]
    
    • updateConstraintsIfNeeded 如果有被setNeedsUpdateConstraints标记的更新,立即在当前Cycle调用updateConstraints
    [self.subView updateConstraintsIfNeeded];
    

    2.布局阶段

    - (void)layoutSubviews;
    

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

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

    • 第一次addSubview的时候,改变frame的时候(view已经addSubview 且两次变化值不能一样)
    UIView *subView = [[UIView alloc] init];
    [self.view addSubview:subView];
    
    • setNeedsLayout,标记更新布局,调用此方法标记,会在下一次runloop来到layoutSubviews,上文的 Layout Engine 也会在检测到约束变化后,通过super. setNeedsLayout()标记,最后从上往下调用layoutSubviews()
    [self.subView setNeedsLayout];
    
    • layoutIfNeeded,如果有标记的更新,立即在当前Drawing Cycle调用layoutSubviews更新视图
     [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

    总结一下:

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

    • 从以上分析得出,自动布局会触发Layout Engine 去计算view的frame,最后重新赋值给view,所以手写的布局(直接设置frame)一般是没有任何效果的;

    获取,修改view.frame

    • 自动布局系统在计算出view的frame以后,会自上而下(父视图-子视图)调用layoutSubviews,要获取期望的frame,因该在layoutSubviews里获取,修改frame

    translatesAutoresizingMaskIntoConstraints

    • 设置为YES,系统会把autoresizingMask 转换为 Constraints,通常我们给代码添加自动布局,记得把该属性设置为NO,这样会避免出现布局不一致

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

    • 初始化约束,尽量写在init,viewDidLoad中,修改.删除约束,可以直接写在对应的事件中,如果对性能有考虑,写在updateConstraints()以获得最佳性能。

    • 不要手动调用layoutSubviews, updateConstraints,这些方法系统会自己触发,若要更新约束,布局,通过setNeedsUpdateConstraints,setNeedsLayout标记更新

    cafe-3537801_1280.jpg

    相关文章

      网友评论

        本文标题:iOS 自动布局触发时机,相关方法调用顺序

        本文链接:https://www.haomeiwen.com/subject/lkqtsktx.html