前言:
开发得跟view打交道,我们也经常看到苹果官方代码有layout方法的相关调用,但是大家可知道什么时候调用,什么时候需要吗?针对网上大部分资料讲得不够清晰,我决定用Demo来讲解
一、layoutSubviews
不能直接调用这个方法。如果强制刷新布局,请调用setNeedsLayout,如果想马上刷新界面,请调用layoutIfNeeded
二、setNeedsLayout跟layoutIfNeded
setNeedsLayout调整视图的子视图的布局时,是在应用程序的主线程调用此方法的。此方法记录调整的布局请求并立即返回。注意,此方法不会强制立即更新,而是等待下一个更新周期才进行刷新页面布局。此行为允许你将所有的布局更新合并到一个更新周期,这很适合用来优化性能。
首先我们在Main.storyboard拖取出一个adjust Height按钮以及带有约束的一个viewRed
如图:
把viewRed的Constraints拖到ViewController.swift,并作为属性
下面我用一段代码验证下
@IBAction func adjustedBtnClick(_ sender: Any) { view.layoutIfNeeded()ifself.redViewHeight.constant ==30.0{self.redViewHeight.constant =self.view.frame.height -90}else{self.redViewHeight.constant =30.0}UIView.animate(withDuration:2.0) {self.view.layoutIfNeeded() } }
当按下按钮时,你看到的第一件事是调用view.layoutIfNeeded()。请记住,此方法强制立即布局并显示更新。你可能会想知道为什么在我们进行约束更改之前调用它呢,因为苹果认为这是一个最佳做法,以确保任何以前的更新等待更新周期的完成,所以我已经添加了它。
当这样的约束被更新时,它会自动执行相当于setNeedsLayout的操作,因此在下一个更新周期内不需要更新视图,所以不用额外添加代码,我们就会看到视图约束更新,但它不会有动画效果。但在我们的例子中,添加了2秒的动画块UIView.animate,在该块内,我们通过layoutIfNeeded方法强制立即布局。由于此布局同步发生,因此在动画块中捕获来自约束更改的帧移动,因此如果你现在运行应用程序,则可以看到红色视图在2秒钟内如何变大变小。
效果如下:
当我们把动画块代码替换成
UIView.animate(withDuration:2.0) {self.view.setNeedsLayout() }
现在我们在动画块中正在做的是将视图标记为需要布局更新,但不会立即强制执行。相反,setNeedsLayout方法返回,视图只是在列表中,以在下一个更新周期中进行更新。净效果是在动画块中不会发生动画,因为该块中的视图没有更改。
效果如下:
在这种情况下单击按钮将立即根据更新的约束更新视图大小,而不是动画更新。等一下,如果我们没有使用layoutIfNeeded,为什么会立即?
对我们而言,红色视图的大小变化立即发生。起初它似乎是反直觉的,因为我们没有使用layoutIfNeeded强制立即更新。但是,我们没有在动画块的上下文中进行视图更新,也就是说刚好是一个周期,所以它似乎是立即的,跟没有UIView.animate这段代码效果是一样的。因为我们标记为更新,更新周期的发生的地方也是动画开始的地方。
因此,由于我们的代码已经标记该视图需要通过setNeedsLayout进行布局更新,所以是在更新周期中立即启动视图更新,而不是从更新周期开始约束更改和帧移动的动画。
刷新子对象布局:
-layoutSubviews方法:这个方法,默认没有做任何事情,需要子类进行重写
------------ 2018.8.25 更新 ------------
这个方法在iOS 5.1和更早版本上没有任何作用。子类可以根据需要重写此方法,以执行更精确的子视图布局。只有当子视图的自动调整、约束的行为不能满足你时,你才应该重写此方法
-setNeedsLayout方法: 标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用
-layoutIfNeeded方法:如果有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)关于在什么的情景会使用该方法,stackoverflow回答的比较详细
关键点
layoutIfNeeded不一定会调用layoutSubviews方法。
setNeedsLayout一定会调用layoutSubviews方法(有延迟,在下一轮runloop结束前)。
如果想在当前runloop中立即刷新,调用顺序应该是
[selfsetNeedsLayout];
[selflayoutIfNeeded];
反之可能会出现布局错误的问题。先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局
在视图第一次显示之前,标记肯定是“需要刷新”的,所以直接调用[view layoutIfNeeded]就会进行立即更新
作者:Dwyane_Coding
链接:https://www.jianshu.com/p/a84f85729952
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。