在 viewWillAppear 中设置约束

Posted

技术标签:

【中文标题】在 viewWillAppear 中设置约束【英文标题】:Setting Constraints in viewWillAppear 【发布时间】:2019-02-21 09:21:14 【问题描述】:

我在viewcontroller 的中心有一个uiview。在此uiview 上方(A)和下方(B)有一个uilabel

我想以编程方式更改此uiview 的底部约束,以便uiview 上方和下方的距离随着屏幕尺寸的变化而彼此相等。因此,计算顶部uiviewuilabel 底部之间的距离(A)和底部uiviewuilabel 顶部之间的距离(B),除以2,然后相应地设置底部约束常数。

我已经完成了计算,但它似乎只有在我将它放在 viewDidAppear 时才有效,并且我需要在用户能够看到对象之前将约束和视图放在正确的位置。这可以在viewWillAppear 中进行吗?感觉直到viewDidAppear 我才能正确计算所有元素。我相信这与autolayout 的生命周期在计算之前没有完成有关。。

let topDistance = view.frame.minY - ALabel.frame.maxY
    let bottomDistance = BLabel.frame.minY - view.frame.maxY
    let total = topDistance + bottomDistance
    bottomConstraint.constant = total / 2

下面是所需结果的前/后示例,其中紫色uiview 对底部橙色uilabel 具有底部约束。例如,当屏幕尺寸增加长度时,我希望底部约束与紫色uiview 和顶部橙色uilabel 之间的距离具有相同的常数。顶部uilabel 对顶部有一个顶部约束,底部uilabel 对底部有一个底部约束。

【问题讨论】:

请添加您想要的图片。 我认为你想要的东西可以在没有计算的情况下设置,只使用静态约束。 您的描述不是很清楚。您有 3 个元素:TopLabel (T)、CenterView (CV)、BottomLabel (B),垂直对齐MainView (MV),对吗?并且您希望从MV 的顶部到T 的顶部的垂直间距相等; T 的底部到 CV 的顶部; CV 的底部到 B 的顶部;和B 的底部到MV 的底部?对吗? @MayurKarmur 我已经添加了图片和解释,这有助于澄清更多吗? @Chris - 抱歉,还是有点混乱。 TopLabel 被限制在 MainView 的顶部?而BottomLabel 被限制在MainView 的底部?如果是这样,那么不要将 Purple 限制为任一标签...只需将其限制为 centerY 【参考方案1】:

尝试回答您的问题:

你说得对,自动布局在viewDidLoad() 甚至viewWillAppear() 中还没有完成它的工作。通常,当您需要进行这样的布局调整时,您希望在viewDidLayoutSubviews() 中进行。来自 Apple 的文档:

viewDidLayoutSubviews

当视图控制器视图的边界发生变化时,视图会调整其子视图的位置,然后系统调用此方法。

但是,如果我了解您实际上想要做什么...

通过使用UIStackView,您可以避免使用任何代码。

在 Vertical UIStackView 中嵌入您的标签、视图、按钮等。将属性设置为:

Alignment: Fill   (or Leading or Center as suits your layout)
Distribution: Equal Spacing
Spacing: 0

将堆栈视图的顶部约束到视图的顶部(加上任何所需的填充)和底部到视图的底部(加上任何所需的填充)。

iPhone SE 尺寸结果:

iPhone 8 尺寸的结果:

iPhone XS 尺寸的结果:

还有,这是 iPhone 8,带有几个不同高度的附加元素:

如您所见,元素之间的垂直间距相等,并且全部由堆栈视图处理。

【讨论】:

堆栈视图将不再起作用,因为我有各种相互关联的组件以及 vc 中的移动部件。话虽如此,看来将代码放在 viewDidLayoutSubviews 中实际上可以在向用户显示之前显示元素的正确位置!【参考方案2】:

在更新约束后写layoutIfNeeded

let topDistance = view.frame.minY - ALabel.frame.maxY
let bottomDistance = BLabel.frame.minY - view.frame.maxY
let total = topDistance + bottomDistance
bottomConstraint.constant = total / 2
self.view.layoutIfNeeded()

【讨论】:

【参考方案3】:

当你使用 View 时确实出现了,当用户看到 VeiwController 时, 使用 viewDidLoad 代替,你不能使用 viewWillAppear ,因为 View 还没有加载。

使用它:

override func viewDidLoad() 
    super.viewDidLoad()

你可以试试打电话

self.view.layoutIfNeeded()

【讨论】:

【参考方案4】:

当我想以编程方式设置约束时,我通常是这样进行的:

let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
v.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true
v.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
v.heightAnchor.constraint(equalToConstant: 100).isActive = true
v.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true

【讨论】:

【参考方案5】:

您可以为此使用调度队列:

      DispatchQueue.main.async 
          let topDistance = view.frame.minY - ALabel.frame.maxY
          let bottomDistance = BLabel.frame.minY - view.frame.maxY
          let total = topDistance + bottomDistance
          bottomConstraint.constant = total / 2
         self.view.layoutIfNeeded()
    

【讨论】:

以上是关于在 viewWillAppear 中设置约束的主要内容,如果未能解决你的问题,请参考以下文章

IOS / Autolayout:在生命周期中为代码中创建的元素设置约束

如何在 Objective C 中的 Xcode 8 中设置约束

从视图控制器动画负 y 原点打破约束

我们如何在 uitableview 单元格中设置标签约束?

在 Android Kotlin 的约束布局中设置适当的约束

代码中设置的自动布局约束未出现在界面生成器中