布局指南与自动布局约束冲突

Layout guide has conflicts with auto layout constraints

我在一本书中输入了一个示例,用于 iOS 编程,但该示例不起作用。示例的结果应该如下图所示:

示例代码如下:

import UIKit

class ViewsController: UIViewController {
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        layoutViews()
    }
    func layoutViews () {
        let v1 = UIView(frame: CGRect(0, 0, 200, 30))
        let v2 = UIView(frame: CGRect(0, 50, 200, 30))
        let v3 = UIView(frame: CGRect(0, 80, 200, 30))
        let v4 = UIView(frame: CGRect(0, 150, 200, 30))
        let views = [v1, v2, v3, v4]
        let colors = [UIColor.red, UIColor.blue, UIColor.yellow, UIColor.green]
        var guides = [UILayoutGuide]()
        
        for (v, c) in zip(views, colors) {
            v.backgroundColor = c
        }
        for v in views {
            v.translatesAutoresizingMaskIntoConstraints = false
            self.view.addSubview(v)
        }
    
        // one fewer guides than views
        for _ in views.dropLast() {
           let g = UILayoutGuide()
            self.view.addLayoutGuide(g)
            guides.append(g)
        }
        
        // guides leading and width are arbitrary
        let anc = self.view.leadingAnchor
        for g in guides {
            g.leadingAnchor.constraint(equalTo: anc).isActive = true
            g.widthAnchor.constraint(equalToConstant: 10).isActive = true
        }
       
        // guides top to previous view
        for (v,g) in zip(views.dropLast(), guides) {
            v.bottomAnchor.constraint(equalTo: g.topAnchor).isActive = true
        }
        // guides bottom to next view
        for (v,g) in zip(views.dropFirst(), guides) {
            v.topAnchor.constraint(equalTo: g.bottomAnchor).isActive = true
        }
        let h = guides[0].heightAnchor
        for g in guides.dropFirst() {
            g.heightAnchor.constraint(equalTo: h).isActive = true
            
        }
    }
}

extension CGRect {
    init(_ x:CGFloat, _ y:CGFloat, _ w:CGFloat, _ h:CGFloat) {
        self.init(x:x, y:y, width:w, height:h)
    }
}
extension CGRect {
    var center : CGPoint {
        return CGPoint(self.midX, self.midY)
    }
}
extension CGRect {
    func centeredRectOfSize(_ sz:CGSize) -> CGRect {
        let c = self.center
        let x = c.x - sz.width/2.0
        let y = c.y - sz.height/2.0
        
        return CGRect(x, y, sz.width, sz.height)
    }
}
extension CGSize {
    init(_ width:CGFloat, _ height:CGFloat) {
        self.init(width:width, height:height)
    }
}
extension CGPoint {
    init(_ x:CGFloat, _ y:CGFloat) {
        self.init(x:x, y:y)
    }
}

extension CGVector {
    init (_ dx:CGFloat, _ dy:CGFloat) {
        self.init(dx:dx, dy:dy)
    }
}

示例中,一开始并没有

这样一行代码
v.translatesAutoresizingMaskIntoConstraints = false

程序运行结果如下图:

并且在控制台中,有以下警告消息:

2021-06-10 10:47:03.267054+1000 Chapter1Views[19879:4731640] [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. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x600003b745f0 h=--& v=--& UIView:0x12ad170a0.minY == 0   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b74550 h=--& v=--& UIView:0x12ad170a0.height == 30   (active)>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b74000 h=--& v=--& UIView:0x12ad16120.minY == 50   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b770c0 h=--& v=--& UIView:0x12ad16120.height == 30   (active)>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b771b0 h=--& v=--& UIView:0x12ad16290.minY == 80   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSLayoutConstraint:0x600003b51e00 UIView:0x12ad170a0.bottom == UILayoutGuide:0x60000215d5e0''.top   (active)>",
    "<NSLayoutConstraint:0x600003b51ea0 UIView:0x12ad16120.bottom == UILayoutGuide:0x60000215d6c0''.top   (active)>",
    "<NSLayoutConstraint:0x600003b51f40 V:[UILayoutGuide:0x60000215d5e0'']-(0)-[UIView:0x12ad16120]   (active)>",
    "<NSLayoutConstraint:0x600003b51f90 V:[UILayoutGuide:0x60000215d6c0'']-(0)-[UIView:0x12ad16290]   (active)>",
    "<NSLayoutConstraint:0x600003b51fe0 UILayoutGuide:0x60000215d6c0''.height == UILayoutGuide:0x60000215d5e0''.height   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003b51f90 V:[UILayoutGuide:0x60000215d6c0'']-(0)-[UIView:0x12ad16290]   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2021-06-10 10:47:03.269574+1000 Chapter1Views[19879:4731640] [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. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x600003b745f0 h=--& v=--& UIView:0x12ad170a0.minY == 0   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b74550 h=--& v=--& UIView:0x12ad170a0.height == 30   (active)>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b74000 h=--& v=--& UIView:0x12ad16120.minY == 50   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b771b0 h=--& v=--& UIView:0x12ad16290.minY == 80   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b77200 h=--& v=--& UIView:0x12ad16290.height == 30   (active)>",
    "<NSAutoresizingMaskLayoutConstraint:0x600003b45b80 h=--& v=--& UIView:0x12ad16670.minY == 150   (active, names: '|':UIView:0x12ad16a00 )>",
    "<NSLayoutConstraint:0x600003b51e00 UIView:0x12ad170a0.bottom == UILayoutGuide:0x60000215d5e0''.top   (active)>",
    "<NSLayoutConstraint:0x600003b51ef0 UIView:0x12ad16290.bottom == UILayoutGuide:0x60000215e220''.top   (active)>",
    "<NSLayoutConstraint:0x600003b51f40 V:[UILayoutGuide:0x60000215d5e0'']-(0)-[UIView:0x12ad16120]   (active)>",
    "<NSLayoutConstraint:0x600003b51e50 V:[UILayoutGuide:0x60000215e220'']-(0)-[UIView:0x12ad16670]   (active)>",
    "<NSLayoutConstraint:0x600003b52030 UILayoutGuide:0x60000215e220''.height == UILayoutGuide:0x60000215d5e0''.height   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003b51e50 V:[UILayoutGuide:0x60000215e220'']-(0)-[UIView:0x12ad16670]   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

这里好像有两种冲突。一个冲突是自动布局指南和视图的自动调整大小掩码之间的冲突,另一个是自动布局指南和视图的自动布局约束之间的冲突。

因此,为了解决第一个冲突,我添加了行代码:

v.translatesAutoresizingMaskIntoConstraints = false

然而,结果是模拟器屏幕上没有任何显示。

我不知道如何解决第二个冲突,即自动布局指南和视图的自动布局约束之间的冲突。

感谢您的帮助!

这是使用 UILayoutGuide “间隔符”实现相等垂直间距的方法,缺少/更正代码:

func layoutViews () {
    let v1 = UIView()
    let v2 = UIView()
    let v3 = UIView()
    let v4 = UIView()
    let views = [v1, v2, v3, v4]
    let colors = [UIColor.red, UIColor.blue, UIColor.yellow, UIColor.green]
    var guides = [UILayoutGuide]()
    
    for (v, c) in zip(views, colors) {
        v.backgroundColor = c
    }
    for v in views {
        v.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(v)
    }
    
    // one fewer guides than views
    for _ in views.dropLast() {
        let g = UILayoutGuide()
        self.view.addLayoutGuide(g)
        guides.append(g)
    }
    
    // respect safe-area
    let safeG = view.safeAreaLayoutGuide
    
    // guides leading and width are arbitrary
    let anc = safeG.leadingAnchor
    for g in guides {
        g.leadingAnchor.constraint(equalTo: anc).isActive = true
        g.widthAnchor.constraint(equalToConstant: 10).isActive = true
    }
    
    // all 4 views constrain leading and trailing
    for v in views {
        v.leadingAnchor.constraint(equalTo: safeG.leadingAnchor, constant: 20.0).isActive = true
        v.trailingAnchor.constraint(equalTo: safeG.trailingAnchor, constant: -20.0).isActive = true
    }
    
    // references to first and last views
    guard let firstView = views.first,
          let lastView = views.last
    else {
        fatalError("Incorrect setup!")
    }
    
    // first view, constrain to top
    firstView.topAnchor.constraint(equalTo: safeG.topAnchor).isActive = true
    
    // last view, constrain to bottom
    lastView.bottomAnchor.constraint(equalTo: safeG.bottomAnchor).isActive = true
    
    // guides top to previous view
    for (v,g) in zip(views.dropLast(), guides) {
        g.topAnchor.constraint(equalTo: v.bottomAnchor).isActive = true
    }
    
    // guides bottom to next view
    for (v,g) in zip(views.dropFirst(), guides) {
        g.bottomAnchor.constraint(equalTo: v.topAnchor).isActive = true
    }
    
    // first view, constrain height to 30
    views[0].heightAnchor.constraint(equalToConstant: 30.0).isActive = true
    
    // remaining views heightAnchor equal to first view heightAnchor
    for v in views.dropFirst() {
        v.heightAnchor.constraint(equalTo: firstView.heightAnchor).isActive = true
    }
    
    let h = guides[0].heightAnchor
    for g in guides.dropFirst() {
        g.heightAnchor.constraint(equalTo: h).isActive = true
    }
}

现在,这是做同样事情的一种方法,但使用 UIStackView 布局:

func layoutViews () {
    
    let stackView = UIStackView()
    stackView.axis = .vertical
    stackView.distribution = .equalSpacing
    
    let colors = [UIColor.red, UIColor.blue, UIColor.yellow, UIColor.green]
    
    colors.forEach { c in
        let v = UIView()
        v.backgroundColor = c
        v.heightAnchor.constraint(equalToConstant: 30.0).isActive = true
        stackView.addArrangedSubview(v)
    }
    
    stackView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(stackView)
    
    // respect safe-area
    let safeG = view.safeAreaLayoutGuide

    NSLayoutConstraint.activate([
        stackView.topAnchor.constraint(equalTo: safeG.topAnchor),
        stackView.leadingAnchor.constraint(equalTo: safeG.leadingAnchor, constant: 20.0),
        stackView.trailingAnchor.constraint(equalTo: safeG.trailingAnchor, constant: -20.0),
        stackView.bottomAnchor.constraint(equalTo: safeG.bottomAnchor),
    ])
}

如您所见...代码行少得多,要处理的对象和约束也少得多。