【问题标题】:Change constraint when device is rotated旋转设备时更改约束
【发布时间】:2021-12-02 16:49:03
【问题描述】:

我在 Xcode 13 中使用以下布局作为自定义弹出窗口UIView(白色背景是透明的):

当屏幕方向更改为横向模式时,顶部和底部的约束仍然是 100pts。正因为如此,中间部分(黄色,UIViewUIStackViewUITableView,...内部)非常小,并且控制台中会出现关于顶部(红色)和底部(蓝色)栏的警告:

无法同时满足约束。

我知道这个警告意味着什么。为了修复它,我创建了以下函数...

private let constraintPortrait:CGFloat = 100
private let constraintLandscape:CGFloat = 10

private func fixConstraints() {
    if (UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) && UIDevice.current.userInterfaceIdiom == .phone {
        topConstraint.constant = constraintLandscape
        bottomConstraint.constant = constraintLandscape
    } else {
        topConstraint.constant = constraintPortrait
        bottomConstraint.constant = constraintPortrait
    }
}

... 并在 viewDidLoadviewDidLayoutSubviews 中调用它。这很好用,但时不时会弹出警告,所以我将打印添加到viewDidLoad,... 并注意到警告实际上是在调用约束修复之前打印的。我将 viewDidLayoutSubviews 重命名为 viewWillLayoutSubviewsUIViewController 生命周期 here)和 Abracadabra!,警告消失了。

人们通常推荐使用viewDidLayoutSubviews,当你想在设备旋转后做一些事情时,但几乎没有提到viewWillLayoutSubviews,在寻找原因时我找到了this answer,说不要使用后者来更改约束,因为它可能会导致另一个自动布局传递。

问题:

我应该改用什么来防止冲突(不更改纵向模式的固定约束!)?有没有办法在界面生成器中自动更改顶部和底部约束,而不使用任何代码,并且仅在实际需要时(-> 始终将 100pts 保持在纵向模式,即使是长桌,但立即切换到 10pts空间不足时在横向模式下)?

【问题讨论】:

  • 您确定错误发生的原因吗?使用您在问题中显示的约束,除非您在某处有固定的宽度/高度约束,否则我不会期望方向的变化会导致无法满足的约束? (或类似的东西)。您可以将自动布局错误发布到问题中吗?或者,这个工具非常适合可视化错误wtfautolayout.com
  • 嗯...又看了一遍...您是否试图将“弹出”视图置于父视图中间?如果是这样,那么执行此操作的限制较少。
  • @Fogmeister 我再次检查了警告,肯定首先提到了红色和蓝色的 UIView (50pts)。之后它抱怨 100pts,然后有一些关于 320pts 的固定高度的东西,我不确定它来自哪里(必须再次检查)。我不能改变红色或蓝色视图的高度,因为我希望整个东西看起来像一个普通的UIAlertController,所以下一个最好的事情是改变 100pts 约束(这有效但可能不是正确的方法它)。
  • 是的,我想将弹出窗口居中放在父视图的中间,但也不希望它变得太大,这就是我使用约束的原因(它适用于小型和大设备)而不是固定高度,对于大型设备来说可能太小而对于小型设备来说太大。
  • 我认为在这里以相反的方式处理这个问题并在弹出视图上设置最大高度/宽度可能是有意义的。所以它的大小是在内部定义的,而不是在外部定义的。然后让它在父视图中居中。如果您想为父视图边缘设置最小间距(您的 100 点约束),那么仍然拥有它们,但将它们设置为 >= 100 而不是 ==100。这意味着您根本不必针对不同的方向更改它们。

标签: ios swift autolayout nslayoutconstraint


【解决方案1】:

viewWillLayoutSubviews 是正确的。您在此处执行的任何布局更改,包括约束更改,都将自动与旋转动画相协调。

但是你怎么知道这个对viewWillLayoutSubviews 的调用是由于轮换引起的?实现这个方法:

https://developer.apple.com/documentation/uikit/uicontentcontainer/1621466-viewwilltransition

或者,在 iPhone 上,这个方法:

https://developer.apple.com/documentation/uikit/uicontentcontainer/1621511-willtransition

我喜欢前者,因为它适用于 iPad 和 iPhone。这些是在viewWillLayoutSubviews 之前调用的,因此您可以设置一个实例属性来向自己发出信号,表明尺寸正在正式更改。您可以通过比较边界大小高度和边界大小宽度来了解发生了什么,并相应地更改约束。

【讨论】:

  • willTransition 也会触发 iPad 吗?我只是用打印添加了它,它似乎工作正常,但它甚至在viewWillLayoutSubviews 之前就被调用了,所以重新绘制不会有同样的问题吗?这两者之间实际上有什么区别?我在谷歌搜索时多次找到willTransition,但人们似乎用它来为他们的UIView 设置动画(例如,让它从右侧滚动),我不需要甚至不想这样做,因为这会引起额外的关注黄色视图不够大。我也不确定如何处理集合或协调器。
  • 对不起,我不小心把事情简单化了。现已修复。
  • 感谢您的编辑。 viewWillTransition 文档说,只要 VC 的 UIView 的大小发生更改,它就会触发,我怎么知道该函数是由于旋转而触发的?我总是检查当前的旋转是什么(见上面的代码),所以我可以简单地记住前一个,比较并采取相应的行动。如果我这样做,是否有任何理由使用willTransition 而不是viewWillLayoutSubviews(或相反)?
  • 我不明白你在问什么。无数的事情可以触发布局。只有旋转等非常戏剧性的事情才能触发viewWillTransition
  • 我用它做了更多的测试,你说得对,willTransition 的触发频率确实低于viewWillLayoutSubviews,到目前为止我只注意到更改旋转后的打印。您是否建议使用willTransition 将轮换与上一个轮换进行比较,以确保绝对确定?
猜你喜欢
  • 2019-03-27
  • 2012-11-24
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多