setNeedsLayout layoutIfNeeded layoutSubviews

在 iOS 开放当中,有一阵我一直好奇于 setNeedsLayout vs layoutIfNeeded vs layoutSubviews 这三个方法的确切功能是什么,我应该什么时候使用他们。

零,提前了解

总所周知,在 iOS 当中,有 RunLoop 的概念,而在我们的主线程,系统会开辟一个主线的 RunLoop,负责一些 UI 渲染,事件处理。 这个概念和 Node 里面的 EventLoop 一个样子,我们在主线程的操作 UI,并不是马上生效,比如,你现在想改变当前 View 的颜色,变成红色。view.background = .red 这句话执行后,这个动作会被放在一个 RunLoop 的事件池里面,我们知道屏幕渲染是一帧一帧,只不过每一帧和每一帧间隔比较短,所以我们肉眼看不出来,我们的 RunLoop 的事件池也是这样的道理,它会一次,一次的清空他的事件池,只不过处理比较快,我们看不出来是异步的。

一,setNeedsLayout

该方法 setNeedsLayout 告诉系统你希望它布局和重绘该视图及其所有子视图,它等待下一个更新周期。这是一个异步活动,该方法完成并立即返回,但是直到稍后一段时间才实际进行布局和重绘,并且不会回调告诉你该更新周期何时。

官方文档:当您要调整视图子视图的布局时,请在应用程序的主线程上调用此方法。此方法记录请求并立即返回。因为此方法不会强制立即更新,而是等待下一个更新周期,所以您可以使用它在更新任何视图之前使多个视图的布局无效。此行为使您可以将所有布局更新合并到一个更新周期,这通常可以提高性能。

二,layoutIfNeeded

相反,方法 layoutIfNeeded 是一个同步调用,它告诉系统您想要一个视图及其子视图的布局和重绘,并且您希望它立即完成而无需等待更新周期。对该方法的调用完成后,已经根据该方法调用之前记录的所有更改对布局进行了调整和绘制。

三,layoutSubviews

默认实现使用您设置的任何约束来确定任何子视图的大小和位置。

子类可以根据需要重写此方法,以更精确地布局其子视图。仅当子视图的自动调整大小和基于约束的行为没有提供所需的行为时,才应覆盖此方法。您可以使用实现直接设置子视图的框架矩形。

您不应该直接调用此方法。如果要强制更新布局,请 setNeedsLayout()在下一次图形更新之前调用该方法。如果要立即更新视图的布局,请调用该 layoutIfNeeded()方法。

四,演示代码

class ViewController: UIViewController {

    @IBOutlet weak var blueHeight: NSLayoutConstraint!

    @IBAction func heightPressed(_ sender: AnyObject) {

    // 在这个方法上面的UI更改,先结算一下,同步渲染完成。 
    // 这个方法之后,这个方法之前的UI更改,就都完成了。
        view.layoutIfNeeded()

        if(self.blueHeight.constant == 25.0)
        {
            self.blueHeight.constant = self.view.bounds.height - 300.0
        }
        else
        {
            self.blueHeight.constant = 25.0
        }

        UIView.animate(withDuration: 2.0, animations: {
        // 在动画的回调里面,强制layout,会使更改的布局产生动画效果。
        // 如果调用 layoutIfNeeded 来强制 layout,不会产生动画效果。
            self.view.layoutIfNeeded()
        })
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

最后更新于