iOS - 在 Swift 3 中以编程方式删除和激活新约束时布局损坏
iOS - Broken layout when removing and activating new constraints programatically in Swift 3
我在 Swift 3 中以编程方式处理约束时遇到了非常令人沮丧的时间。在最基本的层面上,我的应用程序显示了许多具有初始约束的视图,然后在旋转时应用新的约束,允许视图根据需要调整大小和重新定位。不幸的是,这远非易事,因为我对 iOS 开发和 Swift 还是个新手。我花了很多时间尝试 Whosebug 和其他地方提供的许多不同的解决方案,但我一直在达到相同的结果(最后有详细说明)。
我有一个视图控制器(我们称之为 "Main View Controller"),它的根视图包含两个子视图,视图 A 和视图 B 容器。根视图的背景颜色为粉红色。
视图 A 内部包含单个标签,垂直和水平居中,以及橙色背景色。视图 A 有 4 个约束 - [Leading Space to Superview]、[Top Space to Top Layout Guide]、[Trailing Space to Superview] 和 [Bottom Space to Bottom布局指南].
视图 B 容器最初没有内容。它有 4 个约束 - [宽度等于 240]、[高度等于 128]、[Leading Space to Superview] 和 [Leading Space to Superview]。
我还有另一个视图控制器(我们称之为 "View B View Controller"),它驱动视图 B 容器的内容。为了简单起见,这只是一个没有自定义逻辑的默认视图控制器。视图 B 视图控制器的根视图包含一个子视图,视图 B。
视图 B 与上面的视图 A 几乎相同 - 单个标签垂直和水平居中,背景色为蓝色。视图 B 有 4 个约束 - [Leading Space to Superview]、[Top Space to Superview]、[Trailing Space to Superview] 和 [Bottom Space to Superview]。
在主视图控制器 class 中,我维护了对视图 A 和视图 B 容器的 IBOutlet 引用,以及上面提到的它们各自的约束。在下面的代码中,主视图控制器实例化视图 B 视图控制器并将后续视图添加到视图 B 容器,应用灵活的 width/height 自动调整大小掩码以确保它填充可用 space。然后它触发对内部 _layoutContainers() 函数的调用,该函数根据设备的方向执行许多约束修改操作。当前的实现执行以下操作:
- 从视图 A 中删除已知约束
- 从视图 B 容器中删除已知约束
- 根据设备方向,根据特定设计激活视图 A 和视图 B 容器的新约束(在下面的代码注释中有详细说明)
- 针对所有视图触发 updateConstraintsIfNeeded() 和 layoutIfNeeded()
发生调整大小事件时,代码允许触发 viewWillTransition(),然后在完成回调中调用 _layoutContainers() 函数,以便设备处于新状态并可以遵循必要的逻辑路径。
整个主视图控制器单元如下:
import UIKit
class ViewController: UIViewController {
// MARK: Variables
@IBOutlet weak var _viewAView: UIView!
@IBOutlet weak var _viewALeadingConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewATopConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewATrailingConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewABottomConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerView: UIView!
@IBOutlet weak var _viewBContainerWidthConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerTopConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerLeadingConstraint: NSLayoutConstraint!
// MARK: UIViewController Overrides
override func viewDidLoad() {
super.viewDidLoad()
// Instantiate View B's controller
let viewBViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewBViewController")
self.addChildViewController(viewBViewController)
// Instantiate and add View B's new subview
let view = viewBViewController.view
self._viewBContainerView.addSubview(view!)
view!.frame = self._viewBContainerView.bounds
view!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
viewBViewController.didMove(toParentViewController: self)
self._layoutContainers()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil, completion: { _ in
self._layoutContainers()
})
}
// MARK: Internal
private func _layoutContainers() {
// Remove View A constraints
self._viewAView.removeConstraints([
self._viewALeadingConstraint,
self._viewATopConstraint,
self._viewATrailingConstraint,
self._viewABottomConstraint,
])
// Remove View B Container constraints
var viewBContainerConstraints: [NSLayoutConstraint] = [
self._viewBContainerTopConstraint,
self._viewBContainerLeadingConstraint,
]
if(self._viewBContainerWidthConstraint != nil) {
viewBContainerConstraints.append(self._viewBContainerWidthConstraint)
}
if(self._viewBContainerHeightConstraint != nil) {
viewBContainerConstraints.append(self._viewBContainerHeightConstraint)
}
self._viewBContainerView.removeConstraints(viewBContainerConstraints)
// Portrait:
// View B - 16/9 and to bottom of screen
// View A - anchored to top and filling the remainder of the vertical space
if(UIDevice.current.orientation != .landscapeLeft && UIDevice.current.orientation != .landscapeRight) {
let viewBWidth = self.view.frame.width
let viewBHeight = viewBWidth / (16/9)
let viewAHeight = self.view.frame.height - viewBHeight
// View A - anchored to top and filling the remainder of the vertical space
NSLayoutConstraint.activate([
self._viewAView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
self._viewAView.topAnchor.constraint(equalTo: self.view.topAnchor),
self._viewAView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self._viewAView.bottomAnchor.constraint(equalTo: self._viewBContainerView.topAnchor),
])
// View B - 16/9 and to bottom of screen
NSLayoutConstraint.activate([
self._viewBContainerView.widthAnchor.constraint(equalToConstant: viewBWidth),
self._viewBContainerView.heightAnchor.constraint(equalToConstant: viewBHeight),
self._viewBContainerView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: viewAHeight),
self._viewBContainerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
])
}
// Landscape:
// View B - 2/3 of screen on left
// View A - 1/3 of screen on right
else {
let viewBWidth = self.view.frame.width * (2/3)
// View B - 2/3 of screen on left
NSLayoutConstraint.activate([
self._viewBContainerView.widthAnchor.constraint(equalToConstant: viewBWidth),
self._viewBContainerView.heightAnchor.constraint(equalToConstant: self.view.frame.height),
self._viewBContainerView.topAnchor.constraint(equalTo: self.view.topAnchor),
self._viewBContainerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
])
// View A - 1/3 of screen on right
NSLayoutConstraint.activate([
self._viewAView.leadingAnchor.constraint(equalTo: self._viewBContainerView.trailingAnchor),
self._viewAView.topAnchor.constraint(equalTo: self.view.topAnchor),
self._viewAView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self._viewAView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
}
// Fire off constraints and layout update functions
self.view.updateConstraintsIfNeeded()
self._viewAView.updateConstraintsIfNeeded()
self._viewBContainerView.updateConstraintsIfNeeded()
self.view.layoutIfNeeded()
self._viewAView.layoutIfNeeded()
self._viewBContainerView.layoutIfNeeded()
}
}
我的问题是,尽管应用程序的初始加载显示了预期的结果(视图 B 保持 16/9 的比例并位于屏幕底部,视图 A 占据了剩余的 space ):
任何后续旋转都会完全破坏视图并且不会恢复:
此外,应用程序加载后会引发以下约束警告:
TestResize[1794:51030] [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.
(
"<_UILayoutSupportConstraint:0x600000096c60 _UILayoutGuide:0x7f8d4f414110.height == 0 (active)>",
"<_UILayoutSupportConstraint:0x600000090ae0 V:|-(0)-[_UILayoutGuide:0x7f8d4f414110] (active, names: '|':UIView:0x7f8d4f40f9e0 )>",
"<NSLayoutConstraint:0x600000096990 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f413e60] (active)>",
"<NSLayoutConstraint:0x608000094e10 V:|-(456.062)-[UIView:0x7f8d4f413e60] (active, names: '|':UIView:0x7f8d4f40f9e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096990 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f413e60] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
TestResize[1794:51030] [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:0x600000096940 UIView:0x7f8d4f413e60.leading == UIView:0x7f8d4f40f9e0.leadingMargin (active)>",
"<NSLayoutConstraint:0x608000094e60 H:|-(0)-[UIView:0x7f8d4f413e60] (active, names: '|':UIView:0x7f8d4f40f9e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096940 UIView:0x7f8d4f413e60.leading == UIView:0x7f8d4f40f9e0.leadingMargin (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
TestResize[1794:51030] [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.
(
"<_UILayoutSupportConstraint:0x600000096d50 _UILayoutGuide:0x7f8d4f40f4b0.height == 0 (active)>",
"<_UILayoutSupportConstraint:0x600000096d00 _UILayoutGuide:0x7f8d4f40f4b0.bottom == UIView:0x7f8d4f40f9e0.bottom (active)>",
"<NSLayoutConstraint:0x600000092e30 V:[UIView:0x7f8d4f40fd90]-(0)-[_UILayoutGuide:0x7f8d4f40f4b0] (active)>",
"<NSLayoutConstraint:0x608000092070 UIView:0x7f8d4f40fd90.bottom == UIView:0x7f8d4f413e60.top (active)>",
"<NSLayoutConstraint:0x608000094e10 V:|-(456.062)-[UIView:0x7f8d4f413e60] (active, names: '|':UIView:0x7f8d4f40f9e0 )>",
"<NSLayoutConstraint:0x600000096e40 'UIView-Encapsulated-Layout-Height' UIView:0x7f8d4f40f9e0.height == 667 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000092e30 V:[UIView:0x7f8d4f40fd90]-(0)-[_UILayoutGuide:0x7f8d4f40f4b0] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
TestResize[1794:51030] [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.
(
"<_UILayoutSupportConstraint:0x600000096c60 _UILayoutGuide:0x7f8d4f414110.height == 20 (active)>",
"<_UILayoutSupportConstraint:0x600000090ae0 V:|-(0)-[_UILayoutGuide:0x7f8d4f414110] (active, names: '|':UIView:0x7f8d4f40f9e0 )>",
"<NSLayoutConstraint:0x600000096850 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f40fd90] (active)>",
"<NSLayoutConstraint:0x608000093b50 V:|-(0)-[UIView:0x7f8d4f40fd90] (active, names: '|':UIView:0x7f8d4f40f9e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096850 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f40fd90] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
感谢您阅读到这里!肯定有人遇到(并希望解决)这个或类似的问题。任何帮助将不胜感激!
部分问题在于,在 _layoutContainers
中,您从情节提要中删除了约束并添加了现在的约束,但在随后的轮换中,您没有删除之前添加的约束。您应该存储您创建的新约束,以便下次屏幕旋转时,您可以获取旧约束并删除它们。
此外,从 viewDidLoad
调用 _layoutContainers
在 VC 生命周期中还为时过早,因为视图框架还没有正确的值。您可以创建纵横比约束,这样您就不必手动计算尺寸。
例如
的纵向约束
// View B - 16/9 and to bottom of screen
NSLayoutConstraint.activate([
self._viewBContainerView.heightAnchor.constraint(equalToConstant: self._viewBContainerView.widthAnchor, multiplier: 16.0 / 9.0),
self._viewBContainerView.topAnchor.constraint(equalTo: self.view.bottomAnchor),
self._viewBContainerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
// should there be a constraint for self._viewBContainerView.trailingAnchor?
])
与其尝试添加和删除约束,不如考虑调整优先级来转换视图。
因此,默认布局有一个优先级为 900 的约束。然后添加第二个优先级为 1 的冲突约束。现在要切换显示模式,只需将第二个约束优先级提高到 900 以上,然后返回下方以反转。只需更改优先级,即可轻松在 Interface Builder 中对其进行全部测试。
您也可以将更改放在动画块中以获得漂亮的平滑过渡。
-
要考虑使用的另一件事是尺寸 类。使用它,您可以指定特定约束仅适用于特定方向,因此您可能完全获得所需的行为 'for free',只需在 IB 中全部设置即可。
我在 Swift 3 中以编程方式处理约束时遇到了非常令人沮丧的时间。在最基本的层面上,我的应用程序显示了许多具有初始约束的视图,然后在旋转时应用新的约束,允许视图根据需要调整大小和重新定位。不幸的是,这远非易事,因为我对 iOS 开发和 Swift 还是个新手。我花了很多时间尝试 Whosebug 和其他地方提供的许多不同的解决方案,但我一直在达到相同的结果(最后有详细说明)。
我有一个视图控制器(我们称之为 "Main View Controller"),它的根视图包含两个子视图,视图 A 和视图 B 容器。根视图的背景颜色为粉红色。
视图 A 内部包含单个标签,垂直和水平居中,以及橙色背景色。视图 A 有 4 个约束 - [Leading Space to Superview]、[Top Space to Top Layout Guide]、[Trailing Space to Superview] 和 [Bottom Space to Bottom布局指南].
视图 B 容器最初没有内容。它有 4 个约束 - [宽度等于 240]、[高度等于 128]、[Leading Space to Superview] 和 [Leading Space to Superview]。
我还有另一个视图控制器(我们称之为 "View B View Controller"),它驱动视图 B 容器的内容。为了简单起见,这只是一个没有自定义逻辑的默认视图控制器。视图 B 视图控制器的根视图包含一个子视图,视图 B。
视图 B 与上面的视图 A 几乎相同 - 单个标签垂直和水平居中,背景色为蓝色。视图 B 有 4 个约束 - [Leading Space to Superview]、[Top Space to Superview]、[Trailing Space to Superview] 和 [Bottom Space to Superview]。
在主视图控制器 class 中,我维护了对视图 A 和视图 B 容器的 IBOutlet 引用,以及上面提到的它们各自的约束。在下面的代码中,主视图控制器实例化视图 B 视图控制器并将后续视图添加到视图 B 容器,应用灵活的 width/height 自动调整大小掩码以确保它填充可用 space。然后它触发对内部 _layoutContainers() 函数的调用,该函数根据设备的方向执行许多约束修改操作。当前的实现执行以下操作:
- 从视图 A 中删除已知约束
- 从视图 B 容器中删除已知约束
- 根据设备方向,根据特定设计激活视图 A 和视图 B 容器的新约束(在下面的代码注释中有详细说明)
- 针对所有视图触发 updateConstraintsIfNeeded() 和 layoutIfNeeded()
发生调整大小事件时,代码允许触发 viewWillTransition(),然后在完成回调中调用 _layoutContainers() 函数,以便设备处于新状态并可以遵循必要的逻辑路径。
整个主视图控制器单元如下:
import UIKit
class ViewController: UIViewController {
// MARK: Variables
@IBOutlet weak var _viewAView: UIView!
@IBOutlet weak var _viewALeadingConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewATopConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewATrailingConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewABottomConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerView: UIView!
@IBOutlet weak var _viewBContainerWidthConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerTopConstraint: NSLayoutConstraint!
@IBOutlet weak var _viewBContainerLeadingConstraint: NSLayoutConstraint!
// MARK: UIViewController Overrides
override func viewDidLoad() {
super.viewDidLoad()
// Instantiate View B's controller
let viewBViewController = self.storyboard!.instantiateViewController(withIdentifier: "ViewBViewController")
self.addChildViewController(viewBViewController)
// Instantiate and add View B's new subview
let view = viewBViewController.view
self._viewBContainerView.addSubview(view!)
view!.frame = self._viewBContainerView.bounds
view!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
viewBViewController.didMove(toParentViewController: self)
self._layoutContainers()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil, completion: { _ in
self._layoutContainers()
})
}
// MARK: Internal
private func _layoutContainers() {
// Remove View A constraints
self._viewAView.removeConstraints([
self._viewALeadingConstraint,
self._viewATopConstraint,
self._viewATrailingConstraint,
self._viewABottomConstraint,
])
// Remove View B Container constraints
var viewBContainerConstraints: [NSLayoutConstraint] = [
self._viewBContainerTopConstraint,
self._viewBContainerLeadingConstraint,
]
if(self._viewBContainerWidthConstraint != nil) {
viewBContainerConstraints.append(self._viewBContainerWidthConstraint)
}
if(self._viewBContainerHeightConstraint != nil) {
viewBContainerConstraints.append(self._viewBContainerHeightConstraint)
}
self._viewBContainerView.removeConstraints(viewBContainerConstraints)
// Portrait:
// View B - 16/9 and to bottom of screen
// View A - anchored to top and filling the remainder of the vertical space
if(UIDevice.current.orientation != .landscapeLeft && UIDevice.current.orientation != .landscapeRight) {
let viewBWidth = self.view.frame.width
let viewBHeight = viewBWidth / (16/9)
let viewAHeight = self.view.frame.height - viewBHeight
// View A - anchored to top and filling the remainder of the vertical space
NSLayoutConstraint.activate([
self._viewAView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
self._viewAView.topAnchor.constraint(equalTo: self.view.topAnchor),
self._viewAView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self._viewAView.bottomAnchor.constraint(equalTo: self._viewBContainerView.topAnchor),
])
// View B - 16/9 and to bottom of screen
NSLayoutConstraint.activate([
self._viewBContainerView.widthAnchor.constraint(equalToConstant: viewBWidth),
self._viewBContainerView.heightAnchor.constraint(equalToConstant: viewBHeight),
self._viewBContainerView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: viewAHeight),
self._viewBContainerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
])
}
// Landscape:
// View B - 2/3 of screen on left
// View A - 1/3 of screen on right
else {
let viewBWidth = self.view.frame.width * (2/3)
// View B - 2/3 of screen on left
NSLayoutConstraint.activate([
self._viewBContainerView.widthAnchor.constraint(equalToConstant: viewBWidth),
self._viewBContainerView.heightAnchor.constraint(equalToConstant: self.view.frame.height),
self._viewBContainerView.topAnchor.constraint(equalTo: self.view.topAnchor),
self._viewBContainerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
])
// View A - 1/3 of screen on right
NSLayoutConstraint.activate([
self._viewAView.leadingAnchor.constraint(equalTo: self._viewBContainerView.trailingAnchor),
self._viewAView.topAnchor.constraint(equalTo: self.view.topAnchor),
self._viewAView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self._viewAView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)
])
}
// Fire off constraints and layout update functions
self.view.updateConstraintsIfNeeded()
self._viewAView.updateConstraintsIfNeeded()
self._viewBContainerView.updateConstraintsIfNeeded()
self.view.layoutIfNeeded()
self._viewAView.layoutIfNeeded()
self._viewBContainerView.layoutIfNeeded()
}
}
我的问题是,尽管应用程序的初始加载显示了预期的结果(视图 B 保持 16/9 的比例并位于屏幕底部,视图 A 占据了剩余的 space ):
任何后续旋转都会完全破坏视图并且不会恢复:
此外,应用程序加载后会引发以下约束警告:
TestResize[1794:51030] [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.
(
"<_UILayoutSupportConstraint:0x600000096c60 _UILayoutGuide:0x7f8d4f414110.height == 0 (active)>",
"<_UILayoutSupportConstraint:0x600000090ae0 V:|-(0)-[_UILayoutGuide:0x7f8d4f414110] (active, names: '|':UIView:0x7f8d4f40f9e0 )>",
"<NSLayoutConstraint:0x600000096990 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f413e60] (active)>",
"<NSLayoutConstraint:0x608000094e10 V:|-(456.062)-[UIView:0x7f8d4f413e60] (active, names: '|':UIView:0x7f8d4f40f9e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096990 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f413e60] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
TestResize[1794:51030] [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:0x600000096940 UIView:0x7f8d4f413e60.leading == UIView:0x7f8d4f40f9e0.leadingMargin (active)>",
"<NSLayoutConstraint:0x608000094e60 H:|-(0)-[UIView:0x7f8d4f413e60] (active, names: '|':UIView:0x7f8d4f40f9e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096940 UIView:0x7f8d4f413e60.leading == UIView:0x7f8d4f40f9e0.leadingMargin (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
TestResize[1794:51030] [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.
(
"<_UILayoutSupportConstraint:0x600000096d50 _UILayoutGuide:0x7f8d4f40f4b0.height == 0 (active)>",
"<_UILayoutSupportConstraint:0x600000096d00 _UILayoutGuide:0x7f8d4f40f4b0.bottom == UIView:0x7f8d4f40f9e0.bottom (active)>",
"<NSLayoutConstraint:0x600000092e30 V:[UIView:0x7f8d4f40fd90]-(0)-[_UILayoutGuide:0x7f8d4f40f4b0] (active)>",
"<NSLayoutConstraint:0x608000092070 UIView:0x7f8d4f40fd90.bottom == UIView:0x7f8d4f413e60.top (active)>",
"<NSLayoutConstraint:0x608000094e10 V:|-(456.062)-[UIView:0x7f8d4f413e60] (active, names: '|':UIView:0x7f8d4f40f9e0 )>",
"<NSLayoutConstraint:0x600000096e40 'UIView-Encapsulated-Layout-Height' UIView:0x7f8d4f40f9e0.height == 667 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000092e30 V:[UIView:0x7f8d4f40fd90]-(0)-[_UILayoutGuide:0x7f8d4f40f4b0] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
TestResize[1794:51030] [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.
(
"<_UILayoutSupportConstraint:0x600000096c60 _UILayoutGuide:0x7f8d4f414110.height == 20 (active)>",
"<_UILayoutSupportConstraint:0x600000090ae0 V:|-(0)-[_UILayoutGuide:0x7f8d4f414110] (active, names: '|':UIView:0x7f8d4f40f9e0 )>",
"<NSLayoutConstraint:0x600000096850 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f40fd90] (active)>",
"<NSLayoutConstraint:0x608000093b50 V:|-(0)-[UIView:0x7f8d4f40fd90] (active, names: '|':UIView:0x7f8d4f40f9e0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600000096850 V:[_UILayoutGuide:0x7f8d4f414110]-(0)-[UIView:0x7f8d4f40fd90] (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
感谢您阅读到这里!肯定有人遇到(并希望解决)这个或类似的问题。任何帮助将不胜感激!
部分问题在于,在 _layoutContainers
中,您从情节提要中删除了约束并添加了现在的约束,但在随后的轮换中,您没有删除之前添加的约束。您应该存储您创建的新约束,以便下次屏幕旋转时,您可以获取旧约束并删除它们。
此外,从 viewDidLoad
调用 _layoutContainers
在 VC 生命周期中还为时过早,因为视图框架还没有正确的值。您可以创建纵横比约束,这样您就不必手动计算尺寸。
例如
的纵向约束// View B - 16/9 and to bottom of screen
NSLayoutConstraint.activate([
self._viewBContainerView.heightAnchor.constraint(equalToConstant: self._viewBContainerView.widthAnchor, multiplier: 16.0 / 9.0),
self._viewBContainerView.topAnchor.constraint(equalTo: self.view.bottomAnchor),
self._viewBContainerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
// should there be a constraint for self._viewBContainerView.trailingAnchor?
])
与其尝试添加和删除约束,不如考虑调整优先级来转换视图。
因此,默认布局有一个优先级为 900 的约束。然后添加第二个优先级为 1 的冲突约束。现在要切换显示模式,只需将第二个约束优先级提高到 900 以上,然后返回下方以反转。只需更改优先级,即可轻松在 Interface Builder 中对其进行全部测试。
您也可以将更改放在动画块中以获得漂亮的平滑过渡。
-
要考虑使用的另一件事是尺寸 类。使用它,您可以指定特定约束仅适用于特定方向,因此您可能完全获得所需的行为 'for free',只需在 IB 中全部设置即可。