ios - 屏幕旋转的编程约束(swift)
问题描述
我有 2 个 UIView,cv1 和 cv2。在纵向时,我希望 cv1 占据屏幕的上半部分,而 cv2 占据下半部分。当旋转成横向时,我希望 cv1 占据左半部分,而 cv2 占据右半部分,如下所示:
这是我的设置方式(ChildView1
和ChildView2
类只提供颜色和圆角):
class ViewController: UIViewController {
let cv1 = ChildView1()
let cv2 = ChildView2()
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
var safeAreaHeight: CGFloat {
if #available(iOS 11.0, *) {
return view.safeAreaLayoutGuide.layoutFrame.size.height
}
return view.bounds.height
}
//debugPrint("height = \(safeAreaHeight)")
var safeAreaWidth: CGFloat {
if #available(iOS 11.0, *) {
return view.safeAreaLayoutGuide.layoutFrame.size.width
}
return view.bounds.width
}
//debugPrint("width = \(safeAreaWidth)")
cv1.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
cv1.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
if UIDevice.current.orientation == .portrait || UIDevice.current.orientation == .portraitUpsideDown {
cv1.heightAnchor.constraint(equalToConstant: safeAreaHeight / 2).isActive = true
cv1.widthAnchor.constraint(equalToConstant: safeAreaWidth).isActive = true
cv2.topAnchor.constraint(equalTo: cv1.bottomAnchor, constant: 0).isActive = true
cv2.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
//debugPrint("Portrait: height = \(safeAreaHeight), width = \(safeAreaWidth)")
} else if UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight {
cv1.heightAnchor.constraint(equalToConstant: safeAreaHeight).isActive = true
cv1.widthAnchor.constraint(equalToConstant: safeAreaWidth / 2).isActive = true
cv2.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
cv2.leftAnchor.constraint(equalTo: cv1.rightAnchor, constant: 0).isActive = true
//debugPrint("Landscape: height = \(safeAreaHeight), width = \(safeAreaWidth)")
}
cv2.heightAnchor.constraint(equalTo: cv1.heightAnchor).isActive = true
cv2.widthAnchor.constraint(equalTo: cv1.widthAnchor).isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
addCV1()
addCV2()
}
func addCV1() {
self.view.addSubview(cv1)
cv1.translatesAutoresizingMaskIntoConstraints = false
}
func addCV2() {
self.view.addSubview(cv2)
cv2.translatesAutoresizingMaskIntoConstraints = false
}
}
如果我从纵向或横向开始,它看起来不错。但是当我旋转屏幕时,两个视图都会消失,并且每个约束都会收到以下错误消息:
2020-08-11 10:28:55.328063-0600 RotateScreenTesting[91471:4449618] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
"Portrait: height = 603.0, width = 375.0"
2020-08-11 10:29:15.046153-0600 RotateScreenTesting[91471:4449618] [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:0x600003d74500 RotateScreenTesting.ChildView1:0x7fceba30a720.height == 301.5 (active)>",
"<NSLayoutConstraint:0x600003d6d180 RotateScreenTesting.ChildView1:0x7fceba30a720.height == 343 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600003d6d180 RotateScreenTesting.ChildView1:0x7fceba30a720.height == 343 (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.
(lldb)
我究竟做错了什么???
解决方案
重要的是要记住,我们应该使用尺寸布局,而不是纵向/横向。使用多任务滑动/拆分视图,让您的视图具有与设备本身不同的高度:宽度比并不罕见。
有多种方法可以处理这个问题......这是一种方法。
- 创建一组“宽布局”约束
- 创建一组“窄布局”约束
- 创建一系列“常见”约束——这些约束适用于窄或宽“方向”
- 根据视图的大小激活/停用宽和窄约束
试试这个例子:
class ChildView1: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = .blue
layer.cornerRadius = 16
}
}
class ChildView2: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
backgroundColor = .red
layer.cornerRadius = 16
}
}
class SampleViewController: UIViewController {
let cv1 = ChildView1()
let cv2 = ChildView2()
// array of constraints for "wide" layout
var wideConstraints: [NSLayoutConstraint] = []
// array of constraints for "narrow" layout
var narrowConstraints: [NSLayoutConstraint] = []
// just for clarity, array of constraints that apply for
// both wide and narrow layouts
var commonConstraints: [NSLayoutConstraint] = []
override func viewDidLoad() {
super.viewDidLoad()
cv1.translatesAutoresizingMaskIntoConstraints = false
cv2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(cv1)
view.addSubview(cv2)
let g = view.safeAreaLayoutGuide
commonConstraints = [
// cv1 will always be constrained top and leading
cv1.topAnchor.constraint(equalTo: g.topAnchor),
cv1.leadingAnchor.constraint(equalTo: g.leadingAnchor),
// cv2 will always be constrained trailing and bottom
cv2.trailingAnchor.constraint(equalTo: g.trailingAnchor),
cv2.bottomAnchor.constraint(equalTo: g.bottomAnchor),
]
// when narrow, cv1 on top of cv2
narrowConstraints = [
// constrain cv1 trailing
cv1.trailingAnchor.constraint(equalTo: g.trailingAnchor),
// constrain cv2 leading
cv2.leadingAnchor.constraint(equalTo: g.leadingAnchor),
// constrain cv2 top to cv1 bottom
cv2.topAnchor.constraint(equalTo: cv1.bottomAnchor),
// make them equal heights
cv2.heightAnchor.constraint(equalTo: cv1.heightAnchor),
]
// when wide, cv1 side-by-side cv2
wideConstraints = [
// constrain cv1 bottom
cv1.bottomAnchor.constraint(equalTo: g.bottomAnchor),
// constrain cv2 top
cv2.topAnchor.constraint(equalTo: g.topAnchor),
// constrain cv2 leading to cv1 trailing
cv2.leadingAnchor.constraint(equalTo: cv1.trailingAnchor),
// make them equal widths
cv2.widthAnchor.constraint(equalTo: cv1.widthAnchor),
]
// activate the commonConstraints
NSLayoutConstraint.activate(commonConstraints)
if view.frame.width > view.frame.height {
// wider than tall, so "landscape"
NSLayoutConstraint.activate(wideConstraints)
} else {
// taller than wide
NSLayoutConstraint.activate(narrowConstraints)
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { _ in
if size.width > size.height {
// we're transitioning to wider than tall
NSLayoutConstraint.deactivate(self.narrowConstraints)
NSLayoutConstraint.activate(self.wideConstraints)
} else {
// we're transitioning to taller than wide
NSLayoutConstraint.deactivate(self.wideConstraints)
NSLayoutConstraint.activate(self.narrowConstraints)
}
}, completion: {
_ in
// if you want to do somwthing after the transition
})
}
}
推荐阅读
- python - 在字符串列表中查找邻居
- javascript - 更新数据库上深层嵌套数据的正确方法是什么
- javascript - vue-select 允许多个重复项
- macos - macOS Catalina 上的应用程序“无法打开,因为无法验证其完整性”
- python - 你怎么知道一个 Locust 实例是作为 master 还是 worker 运行?
- bash - 如何在 awk NR 中传递变量值
- python - 我如何只在 Python 中循环某个东西?
- matlab - 按组快速求和的矩阵索引
- python - Python 从 Sagemaker model.tar.gz 获取模型估计器对象(用于预测实例解释)
- c# - 如何根据玩家方向调整对象旋转?