无法满足嵌套堆栈视图的约束
Unable to satisfy constraints with nested stack views
我在尝试调试控制台中的 Unable to satisfy constraints
警告时逐渐变得疯狂,该警告在我从 iPhone 纵向转换为 iPhone 横向时出现。我正在处理带有嵌套堆栈视图的仪表板类型视图。如果宽度大小 class 是规则的,则顶部堆栈将轴更改为 .horizontal。
查看层次结构:
- 查看
- 滚动视图
- 基本视图
- 仪表板堆栈视图
- 顶部堆栈视图
- 进度视图
- 统计视图
- 中间视图
- 底视图
代码
- 所有组件在
viewDidLoad
中有translatesAutoresizingMaskIntoConstraints = false
- 当检测到大小 class 变化时,约束被操纵
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.
依赖于其他布局操作的约束可能会变得棘手。自动布局会产生多个 "passes",因为它试图满足所有约束。
这对于比例约束和可能受堆栈视图影响的约束(它们正在执行自己的布局操作)尤为明显。
为了避免警告,您通常需要调整约束优先级。
并且,您在激活/停用约束时需要注意顺序。
我按照你展示的方式布置了对象,对于你的情况,你应该能够通过以下方式解决问题:
- 将
progressViewHeight
、progressViewWidth
和 topStackHeight
约束的优先级分别更改为 999
。
- 约束变化顺序应该是
- 在 views/labels/etc 上设置
.isActive = false
应该先进行
- 下一步应该更改 views/labels/etc 上的
.constant
值
- 然后在 views/labels/etc
上设置 .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()
}
我在尝试调试控制台中的 Unable to satisfy constraints
警告时逐渐变得疯狂,该警告在我从 iPhone 纵向转换为 iPhone 横向时出现。我正在处理带有嵌套堆栈视图的仪表板类型视图。如果宽度大小 class 是规则的,则顶部堆栈将轴更改为 .horizontal。
查看层次结构:
- 查看
- 滚动视图
- 基本视图
- 仪表板堆栈视图
- 顶部堆栈视图
- 进度视图
- 统计视图
- 中间视图
- 底视图
- 顶部堆栈视图
- 仪表板堆栈视图
- 基本视图
- 滚动视图
代码
- 所有组件在
viewDidLoad
中有 - 当检测到大小 class 变化时,约束被操纵
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.
依赖于其他布局操作的约束可能会变得棘手。自动布局会产生多个 "passes",因为它试图满足所有约束。
这对于比例约束和可能受堆栈视图影响的约束(它们正在执行自己的布局操作)尤为明显。
为了避免警告,您通常需要调整约束优先级。
并且,您在激活/停用约束时需要注意顺序。
我按照你展示的方式布置了对象,对于你的情况,你应该能够通过以下方式解决问题:
- 将
progressViewHeight
、progressViewWidth
和topStackHeight
约束的优先级分别更改为999
。 - 约束变化顺序应该是
- 在 views/labels/etc 上设置
.isActive = false
应该先进行 - 下一步应该更改 views/labels/etc 上的
.constant
值 - 然后在 views/labels/etc 上设置
- 然后更改
.constant
堆栈视图中的值 - 最后更改堆栈视图的
.axis
.isActive = true
- 在 views/labels/etc 上设置
并非所有这些都是硬性规定,但根据经验,这是一个很好的方法。
尝试更改上面列出的约束的优先级,并按如下方式更改 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()
}