转自:https://github.com/NemoAir/AutoLayoutFlowDemo
viewcontroller中和 “约束-布局-渲染” 过程有关的主要方法和流程。
1. -initWithNibName
如果使用 StoryBoard 加载 ViewController ,则会直接调用 -initWithCoder 方法,而不会调用该方法。
而通过加载 Xib 或者直接 -init 都会调用这个方法
2. -loadView
3. -viewDidLoad
这里需要注意,在 iOS8 中,Xib文件中的view在这个阶段并不会加载到self.view的层次结构中。所以这
个时候如果进行布局或者约束的生成和改变是无效的,而 iOS7 这个时候已经将 Xib 里的 view 加到 self.view 的层次结构中
4. -viewWillAppear
5. -viewWillLayoutSubviews
这个方法会使self.view 调用layoutSubviews
6. [self.view layoutSubviews]
调用了这个方法后,就会对 self.view 的 subviews 也调用
-updateConstraint
方法,更新约束。在iOS8中,这个时候Xib中加载的view已经加入到self.view的层次结构中
8. [self updateConstranits];
9. -viewDidLayoutSubviews
10. -viewDidAppear
11. -viewWillDisappear
12. -viewDidDisappear
13. -dealloc
首先自动布局发生的顺序是:
1.更新约束。
2.依赖约束系统计算出subviews的frame开始布局。
3.根据计算出的frame重新在屏幕上渲染。
在第2步的过程中可能重复触发第一步的约束更新操作,也就是在发生布局的时候可能还会触发更新约束的过程,高级的约束可能会出现这种情况,同时这种情况要注意不要形成死循环。 所以不能在第一步的约束更新操作中配置布局,例如调用
[self.view layoutIfNeeded]
来触发布局,这样可能会造成死循环。
约束和布局的几个相关方法
1) [self.view setNeedsUpdateConstraints]
2) [self.view updateConstraintsIfNeeded]
3) [self.view setNeedsLayout]
4) [self.view layoutIfNeeded]
5) [self.view setNeedsDisplay]
5)
[self.view setNeedsDisplay] 这个方法和渲染有关,但是因为名字比较相似,我自己也混淆过,所以也一起说明下
1)
[self.view setNeedsUpdateConstraints] 告诉约束系统view约束系统需要更新
2)
[self.view updateConstraintsIfNeeded] 立即更新view的约束
这两个方法都是针对约束的操作方法。 1)调用后告诉系统该view上的约束有更新,系统相当于有一个需要更新该view的标记flag,在下次 进行计算或更新约束的时候.比如调用了2)就会调用viewcontrollerde
-updateViewConstraints
但是这里有个坑需要注意。如果单独调用2)不一定会触发updateViewConstraints。 因为 1)和 2)的实现类似下面布局更新方法的实现原理:
-(void)layoutIfNeeded {
if (self._needsLayout) {
UIView *sv = self.superview;
if (sv._needsLayout) {
[sv layoutIfNeeded];
} else {
[self layoutSubviews];
}
}
}
所以在更新约束的时候要 1)、 2)一起调用才能保证约束立刻被计算。 而且在任何地方对约束进行改变都会触发 1)的方法。 1)会调用2),2)又会触发 viewController的-updateViewConstraints方法,所以最后还可以在updateViewConstraints方法中最后配置一 次布局约束。但是我经过考虑,在需要触发约束改变的地方就直接改变约束会比较好。-updateViewConstraints 方法作为最后一步来补 救约束的选择。 同时约束的计算是从subview到superview的,这点和布局、渲染到屏幕的过程刚好相反。布局和渲染是从superview到subview中来进行的。
3)
[self.view setNeedsLayout] 标记view布局需要更新
4)
[self.view layoutIfNeeded] 立即更新view的布局
这两个方法是对布局进行改变的方法。他们合作的原理和1)、2)一样。3)负责标记需要布局改变,4)来触发
-layoutSubviews
但是调用 4) 的时候会判断是 否需要更新布局的 flag ,就是如果没有调用3)有可能4)并不会触发layoutSubviews。所以在实践中3)、4)也最好一 起使用,才能保证布局更新。这两个方法加入动画块就可以实现动画布局的效果。 比如你更新好约束后,这样调用:
[UIView animateWithDuration:0.4 animations:^{
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
}];
就可以动画改变布局。
在viewcontroller中调用 3)和4)会重新触发viewWillLayoutSubviews,viewDidLayoutSubviews的过程,所以最好不要在这两个方法
中加入布局的代码,应该完全依靠约束来保证布局,因为这两个方法在每次布局过程中都会被调用。
同时布局frame的计算过程是从 superview 到 subview 的。而 -layoutSubviews 方法被触发后,会对该 view 的 subviews 进行
-layoutIfNeeded
的递归操作。所以布局 frame 的计算是从 superView 到 subView 的。
这里要明白,-layoutSubviews方法是最后一步可以补救布局修改frame的地方,最好在前面的步骤中就通过约束来完成布局
,除非万不得已才在自定义 view中重载-layoutSubview方法来完成最后一步布局。这所有的过程都是前后逻辑依赖的,frame
的计算会依赖约束的计算,所以在布局的阶段可能会重新调用约束的计算的阶段。当最后布局完成后就是屏幕从上到下的渲染
过程。这个过程涉及到的就是下面的 5)。
5) 调用后会触发view的 -drawRect 方法重绘,和布局没有关系。调用这个方法后 -drawRect 方法何时调用系统决定,开发者则不用操心,系统对重绘有自己的优化方法。 实时绘图需要在
-touchBegan
方法中调用 5)来实时刷新屏幕。
所以实践中的情况是:
a.如果手动直接改变了约束,直接调用布局更新方法 3)即可,因为 3)调用后在触发布局的时候会自动调用约束计算方法 2)计算最新的约束。
b.如果在某些情况下在重载 -updateViewConstraints 的方法中需要改变约束,调用 1),而且大多数情况下接着调用 3)。
c.如果在改变约束后需要马上重新布局,那么在上述的操作后立马调用 4)来使新的约束生效。在动画 block 中调用就会产生动画效果。