iOS Autolayout自适应UI问题

Haa*_*nti 9 ios autolayout swift adaptive-layout

我想根据宽度是否大于高度来改变我的布局.但我不断收到布局警告.我在自适应布局上观看了两个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()
         }

     } 

}
Run Code Online (Sandbox Code Playgroud)

当我旋转几次时,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)>
Run Code Online (Sandbox Code Playgroud)

Mik*_*and 4

你是对的,你太早做约束工作了。...<NSLayoutConstraint:0x600000085d70 'UIView-Encapsulated-Layout-Height' UIView:0x7ff68ec03f50.height == 414是系统为视图控制器的横向视图高度创建的约束 - 为什么它是 414,恰好是 iPhone 5.5 (6/7 Plus) 横向高度。旋转尚未开始,垂直约束与 450 高度约束冲突,您会收到警告。

因此,您必须在旋转完成后添加约束。viewWillLayoutSubviews是逻辑位置,因为当视图大小准备好但在屏幕上出现任何内容之前,它会被调用,并且可以保存系统本来会进行的布局传递。

但是,要消除警告,您仍然需要删除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()
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为您还应该始终删除“viewWillLayoutSubviews”中的约束,因为在某些情况下视图将在不转换的情况下进行布局。 (2认同)