无法满足嵌套堆栈视图的约束

Posted

技术标签:

【中文标题】无法满足嵌套堆栈视图的约束【英文标题】:Unable to satisfy constraints with nested stack views 【发布时间】:2020-02-26 11:56:17 【问题描述】:

我在尝试在控制台中调试 Unable to satisfy constraints 警告时会慢慢发疯,当我从 iPhone 纵向转换到 iPhone 横向时会出现该警告。我正在使用带有嵌套堆栈视图的仪表板类型视图。如果宽度大小类是常规的,则顶部堆栈将轴更改为 .horizo​​ntal。

查看层次结构:

查看 滚动视图 基本视图 仪表板堆栈视图 顶部堆栈视图 进度视图 统计视图 中间视图 底视图

代码

所有组件在viewDidLoad 中都有translatesAutoresizingMaskIntoConstraints = false 在检测到大小类更改时会操纵约束
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) 

    setupDashboardView()


func setupDashboardView() 
    let sizeClass = self.traitCollection

    if sizeClass.isIpad 
        topStack.axis = .horizontal
        topStackHeight.constant = (self.dashboardStack.bounds.height*0.3)
        progressViewWidth.constant = (self.dashboardStack.bounds.width*0.25)
        progressViewHeight.isActive = false
     else if sizeClass.isIphonePortrait 
        topStack.axis = .vertical
        topStackHeight.constant = self.view.frame.width*1.5
        progressViewWidth.isActive = false
        progressViewHeight.isActive = true
        progressViewHeight.constant = self.view.frame.width
     else if sizeClass.isIphoneLandscape 
        progressViewHeight.isActive = false
        topStack.axis = .horizontal
        topStackHeight.constant = self.view.frame.height*0.5
        progressViewWidth.isActive = true
        progressViewWidth.constant = self.view.frame.width*0.25
        
        dashboardStack.spacing = 5
        topStack.spacing = 5

        self.updateViewConstraints()
        self.viewDidLayoutSubviews()
    


extension UITraitCollection 
    var isIpad: Bool 
        return horizontalSizeClass == .regular && verticalSizeClass == .regular
    
    var isIphoneLandscape: Bool 
        return verticalSizeClass == .compact
    
    var isIphonePortrait: Bool 
        return horizontalSizeClass == .compact && verticalSizeClass == .regular
    
    var isIphone: Bool 
        return isIphoneLandscape || isIphonePortrait
    


//For constraint debugging
extension NSLayoutConstraint 
    override public var description: String 
        let id = identifier ?? ""
        return "id: \(id), constant: \(constant)"
    

调试器输出

当我将 iPhone 从纵向旋转到横向时收到此信息。

[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. 
(
    "id: , constant: 0.0",
    "id: , constant: 0.0",
    "id: , constant: 0.0",
    "id: , constant: 0.0",
    "id: progressWidth, constant: 224.0",
    "id: s, constant: 5.0",
    "id: scrollTrail, constant: 5.0",
    "id: UIScrollView-frameLayoutGuide-width, constant: 0.0",
    "id: UISV-canvas-connection, constant: 0.0",
    "id: UISV-canvas-connection, constant: 0.0",
    "id: UISV-canvas-connection, constant: 0.0",
    "id: UIView-Encapsulated-Layout-Width, constant: 896.0",
    "id: UIViewSafeAreaLayoutGuide-left, constant: 44.0",
    "id: UIViewSafeAreaLayoutGuide-right, constant: 44.0"
)

Will attempt to recover by breaking constraint 
id: progressWidth, constant: 224.0

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.
2020-02-26 11:27:51.128311+0000 Haem Data PG Std[9632:2228108] [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. 
(
    "id: progressHeight, constant: 414.0",
    "id: topStackHeight, constant: 185.0",
    "id: UISV-canvas-connection, constant: 0.0",
    "id: UISV-canvas-connection, constant: 0.0"
)

Will attempt to recover by breaking constraint 
id: progressHeight, constant: 414.0

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.

【问题讨论】:

我确实认为我们需要查看 TopStackViewProgressView 的约束,以便了解在更改方向时实际激活的内容。 【参考方案1】:

依赖于其他布局操作的约束可能会变得棘手。自动布局在尝试满足所有约束时会进行多次“通过”。

这对于比例约束和可能受堆栈视图影响的约束(它们正在执行自己的布局操作)尤其明显。

为避免出现警告,您通常需要调整约束优先级。

而且,您在激活/停用约束时需要注意顺序。

我按照您展示的方式布置了对象,对于您的情况,您应该能够通过以下方式解决问题:

progressViewHeightprogressViewWidthtopStackHeight 约束的优先级分别更改为999。 约束更改顺序应该是 应首先在视图/标签/等上设置 .isActive = false 接下来应该更改视图/标签/等上的 .constant 值 然后在视图/标签/等上设置.isActive = true 随后更改堆栈视图上的 .constant 值 最后更改堆栈视图的.axis

并非所有这些都是一成不变的规则,但根据经验,这是一个很好的方法。

尝试更改上面列出的约束的优先级,并更改您的 setupDashboardView() 函数,如下所示:

func setupDashboardView() 
    let sizeClass = self.traitCollection

    if sizeClass.isIpad 
        progressViewHeight.isActive = false
        progressViewWidth.constant = (self.dashboardStack.bounds.width*0.25)
        topStackHeight.constant = (self.dashboardStack.bounds.height*0.3)
        topStack.axis = .horizontal
     else if sizeClass.isIphonePortrait 
        progressViewWidth.isActive = false
        progressViewHeight.isActive = true
        progressViewHeight.constant = self.view.frame.width
        topStackHeight.constant = self.view.frame.width*1.5
        topStack.axis = .vertical
     else if sizeClass.isIphoneLandscape 
        progressViewHeight.isActive = false
        progressViewWidth.constant = self.view.frame.width*0.25
        progressViewWidth.isActive = true
        topStackHeight.constant = self.view.frame.height*0.5
        topStack.axis = .horizontal
    
    dashboardStack.spacing = 5
    topStack.spacing = 5

    // not needed
    //self.updateViewConstraints()

    // NEVER call this yourself
    //self.viewDidLayoutSubviews()

【讨论】:

以上是关于无法满足嵌套堆栈视图的约束的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xcode 的嵌入式堆栈视图中设置边距而不会出现约束错误?

尝试以编程方式使用嵌套的 Stack 视图制作网格

如何为堆栈视图中的视图添加约束

XCode:堆栈视图和约束

UIStackView“无法同时满足约束”“压扁”隐藏视图

无法满足约束时如何识别调试区域中的视图?