强制自动布局以在viewDidLoad上正确更新UIView框架

时间:2017-09-15 01:30:19

标签: ios swift uiview autolayout viewdidload

尽管可能很简单,但我仍然发现自己正在努力解决正确的问题。

我试图了解在中找到 REAL UIView(或任何其他子视图)框架的正确方法是什么使用自动布局时,viewDidLoad

主要问题是在viewDidLoad中,视图未应用其约束。我知道"已知"这种情况的答案是

override func viewDidLoad() {
        super.viewDidLoad()
     view.layoutIfNeeded()
     stepContainer.layoutIfNeeded() // We need this Subview real frame!
     let realFrame = stepContainer.frame
}

但我发现它并不总是有效,并且不时会给出错误的帧(即不是显示的最终帧)。

经过一些研究后,我发现在DispatchQueue.main.async { }下扭曲此代码可以得到准确的结果。但是我不确定它是否是正确的处理方式,或者我是否使用此方法导致了一些引擎盖下的问题。决赛"工作"代码:

override func viewDidLoad() {
        super.viewDidLoad() 

        DispatchQueue.main.async {
            self. stepContainer.layoutIfNeeded()
            print(self. stepContainer.frame) // Real frame..
        }

}

注意:我只需要从viewDidLoad中找到真正的帧,请不要建议使用viewDidAppear / layoutSubviews等。

3 个答案:

答案 0 :(得分:6)

@DavidRönnqvist指出

  

调度异步的原因是这里给出“正确”值   它被安排在下一个运行循环中运行;在viewWillAppear之后   并且viewDidLayoutSubviews已经运行。

示例

override func viewDidLoad() {
  super.viewDidLoad()
  DispatchQueue.main.async {
    print("DispatchQueue.main.async viewDidLoad")
  }
}

override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)

  print("viewWillAppear")
}

override func viewDidAppear(_ animated: Bool) {
  super.viewDidAppear(animated)

  print("viewDidAppear")
}

override func viewDidLayoutSubviews() {
  super.viewDidLayoutSubviews()

  print("viewDidLayoutSubviews")
}
  

viewWillAppear中

     

viewDidLayoutSubviews

     

viewDidAppear

     

DispatchQueue.main.async viewDidLoad

来自DispatchQueue.main.async的{​​{1}}内的代码甚至在viewDidLoad之后调用。因此,在viewDidAppear中使用DispatchQueue.main.async会为您提供正确的框架,但它不是最早的。

<强>答案

  • 如果您希望尽早获得正确的框架,viewDidLoad是正确的做法。

  • 如果您必须viewDidLayoutSubviews内放置一些代码,那么您的做法是正确的。看起来像viewDidLoad是最好的方法。

答案 1 :(得分:1)

阅读完评论后,也许您可​​以尝试将视图控制器嵌入另一个带容器的视图控制器中,并执行以下操作:

  • 在父视图控制器中添加变量:var viewSize : CGSize?
  • 在子视图控制器中添加变量:var parentViewSize : CGSize?
  • 在父视图控制器中,获取viewDidLayoutSubViews中的尺寸并存储它:viewSize = view.size
  • 在父视图控制器中,prepareForSegue发送您存储的尺寸:(destinationViewController as? MyViewController)?.parentViewSize = viewSize
  • 在子视图控制器中,viewDidLoad您将访问parentViewSize变量,其中包含良好的值

会这样做吗?

答案 2 :(得分:1)

您的问题与this problem类似,我的回答也是一样的。

不保证viewDidLoad中的帧与最终显示视图时的帧相同。 UIKit会根据将出现的上下文调整视图控制器视图的框架,然后再显示它。为了更好地理解视图生命周期,您可以参考此图像。 enter image description here

该图片将帮助您了解代码的工作原理。如图所示,一旦加载了视图,就会调用viewWillAppear,当你设置dispatch async时,它会在执行viewWillAppear后异步添加到线程中。因此,一旦调用了viewWillAppear,您的框架就会根据您的视图进行更新,并且您可以在调度异步中获得正确的框架。

参考文献: Image source

For more information about view life cycle you can visit this answer

所以,最后,你可以选择以下两个选项之一:

  1. 如果要使用自动布局,请在viewDidLayoutSubviews或viewWillAppear中管理帧。 (在这些viewDidLayoutSubview中的任何一个都应该去,因为每次你的视图位于视图层次结构的顶部时都会调用viewWillAppear,这不应该是首选的。)
  2. 如果您要跳过自动布局,则可以通过编程方式创建和维护视图。
  3. 希望这有帮助!