我想根据宽度是否大于高度来更改布局。但我不断收到布局警告。我看了两个关于自适应布局的 WWDC 视频,这很有帮助,但没有解决问题。我使用以下代码创建了一个简单版本的布局。这只是为了让人们可以重现我的问题。代码在 iPhone 7 plus 上运行。
import UIKit
class ViewController: UIViewController {
var view1: UIView!
var view2: UIView!
var constraints = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
view1 = UIView()
view1.translatesAutoresizingMaskIntoConstraints = false
view1.backgroundColor = UIColor.red
view2 = UIView()
view2.translatesAutoresizingMaskIntoConstraints = false
view2.backgroundColor = UIColor.green
view.addSubview(view1)
view.addSubview(view2)
layoutPortrait()
}
func layoutPortrait() {
let views = [
"a1": view1,
"a2": view2
]
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[a1]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[a2]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[a1(450)][a2]|", options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)
}
func layoutLandscape() {
let views = [
"a1": view1,
"a2": view2
]
constraints += NSLayoutConstraint.constraints(withVisualFormat: "H:|[a1(a2)][a2(a1)]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[a1]|", options: [], metrics: nil, views: views)
constraints += NSLayoutConstraint.constraints(withVisualFormat: "V:|[a2]|", options: [], metrics: nil, views: views)
NSLayoutConstraint.activate(constraints)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
NSLayoutConstraint.deactivate(constraints)
constraints.removeAll()
if (size.width > size.height) {
layoutLandscape()
} else {
layoutPortrait()
}
}
}
当我旋转几次时,xcode 会记录警告。我想我把布局切换到早期,因为它仍在变化,但我已经将纵向高度设置为大或其他。有人知道我做错了什么吗?
约束警告:(从横向返回纵向时发生)
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x618000081900 V:|-(0)-[UIView:0x7ff68ee0ac40] (active, names: '|':UIView:0x7ff68ec03f50 )>",
"<NSLayoutConstraint:0x618000081d60 UIView:0x7ff68ee0ac40.height == 450 (active)>",
"<NSLayoutConstraint:0x618000083cf0 V:[UIView:0x7ff68ee0ac40]-(0)-[UIView:0x7ff68ee0ade0] (active)>",
"<NSLayoutConstraint:0x618000083d40 V:[UIView:0x7ff68ee0ade0]-(0)-| (active, names: '|':UIView:0x7ff68ec03f50 )>",
"<NSLayoutConstraint:0x600000085d70 'UIView-Encapsulated-Layout-Height' UIView:0x7ff68ec03f50.height == 414 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x618000081d60 UIView:0x7ff68ee0ac40.height == 450 (active)>
最佳答案
你是对的,你做约束工作太早了。 <NSLayoutConstraint:0x600000085d70 'UIView-Encapsulated-Layout-Height' UIView:0x7ff68ec03f50.height == 414
...
是系统为 View Controller 的横向 View 高度创建的约束 - 为什么它是 414,正好是 iPhone 5.5(6/7 Plus)横向高度。旋转尚未开始,垂直约束与 450 高度约束冲突,您收到警告。
因此您必须在旋转完成后添加约束。 viewWillLayoutSubviews
是合乎逻辑的地方,因为当 View 大小准备好但在屏幕上出现任何内容之前会调用它,并且可以保存系统它本来可以进行的布局传递。
但是,要消除警告,您仍然需要删除 viewWillTransition(to size: with coordinator:)
中的约束.系统在 viewWillLayoutSubviews()
之前进行新的自动布局计算被调用,当从肖像转到风景时,您会以另一种方式收到警告。
编辑:正如@sulthan 在评论中指出的那样,由于其他事情可以触发布局,因此您还应该始终在添加约束之前删除它们。如果[constraints]
数组被清空没有效果。由于停用后清空数组是一个关键步骤,它应该在自己的方法中,所以 clearConstraints()
.
此外,请记住检查初始布局中的方向 - 您调用 layoutPortrait()
在viewDidLoad()
想当然这不是问题。
新/更改方法:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// Should be inside layoutPortrait/Landscape methods, here to keep code sample short.
clearConstraints()
if (self.view.frame.size.width > self.view.frame.size.height) {
layoutLandscape()
} else {
layoutPortrait()
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
clearConstraints()
}
/// Ensure array is emptied when constraints are deactivated. Can be called repeatedly.
func clearConstraints() {
NSLayoutConstraint.deactivate(constraints)
constraints.removeAll()
}
关于iOS Autolayout 自适应 UI 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41656944/