为了账号安全,请及时绑定邮箱和手机立即绑定

setNeedsLayout和layoutIfNeeded看我就懂!

标签:
iOS native Swift

前言:

开发得跟view打交道,我们也经常看到苹果官方代码有layout方法的相关调用,但是大家可知道什么时候调用,什么时候需要吗?针对网上大部分资料讲得不够清晰,我决定用Demo来讲解

一、layoutSubviews

不能直接调用这个方法。如果强制刷新布局,请调用 setNeedsLayout,如果想马上刷新界面,请调用layoutIfNeeded

二、setNeedsLayout跟layoutIfNeded

setNeedsLayout调整视图的子视图的布局时,是在应用程序的主线程调用此方法的。此方法记录调整的布局请求并立即返回。注意,此方法不会强制立即更新,而是等待下一个更新周期才进行刷新页面布局。此行为允许你将所有的布局更新合并到一个更新周期,这很适合用来优化性能。

首先我们在Main.storyboard拖取出一个adjust Height按钮以及带有约束的一个viewRed
如图:

viewRedConstraints拖到ViewController.swift,并作为属性

下面我用一段代码验证下

    @IBAction func adjustedBtnClick(_ sender: Any) {
        view.layoutIfNeeded()
        if self.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回答的比较详细

  • 如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局
  • 在视图第一次显示之前,标记肯定是“需要刷新”的,所以直接调用[view layoutIfNeeded]就会进行立即更新
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消