在iOS的UI层,有大量的layout开头的方法,在这里总结一下。
1.layoutSubViews
这个方法,默认没有做任何事情,需要子类进行重写,常用在自定义视图里,比如自定义一个view。那么在什么时候调用呢?
- addSubview会触发layoutSubviews (这是前提,最起码要保证视图被添加出来,下面的都在这个前提下)
- 设置frame的时候会触发,前提是前后的frame发生了变化。
- 滚动一个UIScrollview会触发
- 旋转screen会触发父UIview上的layoutSubViews
- 改变一个UIView大小的时候也会触发父view的layoutSubviews
重写这个方法的时候,不要忘了[super layoutSubViews]。
layoutSubViews一般使用场景:当我们在某个类的内部调整子视图位置时,需要调用。也就是说改变这个类内部视图的位置时候,在这个类内部重写这个方法,进行修改。
2.setNeedsLayout
它的作用是将控件标记为“需要刷新”,但不立即刷新,在系统runloop的下一个周期自动调用layoutSubviews
。
这样的好处是节省性能(等几个控件布局好后,下一次再更新)。
如果更改了控件上的约束,会自动调用setNeedsLayout方法,将其标记为“需要刷新”
3.layoutIfNeeded
调用此方法,如果前面有“需要刷新”的标记,会立即调用layoutSubviews
进行布局,反之则不会。
setNeedSLayout和layoutIfNeeded都是通过调用layoutSubviews进行布局的,因为layoutSubviews是调整其子视图的布局,所以如果要更改子视图的布局,调用setNeedSLayout和layoutIfNeeded都是针对父视图的
举个例子:
1 | class ViewController: UIViewController { |
如上,我们定义了一个红色button和一个黄色的view,点击的时候,更改view的大小。(在这里view我用了Snapkit库来约束,为啥不用frame呢?因为在下面如果更改frame的大小,会直接触发layoutSubviews
方法,从而会直接更新UI并刷新)
所以上面的代码运行后,效果如下:
通过SnapKit库更新backView的约束,默认会调用setNeedsLayout
方法,并将其标记为“需要刷新”,但不立即刷新,在系统runloop的下一个周期自动调用layoutSubviews
。所以在UIView的动画代码块里,由于视图没有更改,所以没有动画效果。
我们把点击方法的代码写成如下:
1 | func clickBtn(btn: UIButton) { |
也就是在动画代码块的最后,让父视图调用layoutIfNeeded
,立即刷新父控件里的所有子控件,对子控件重新布局。由于此布局同步发生,所以在UIview的动画代码块里,视图进行了更改,就有了动画效果。
效果如下: