屏幕旋转的编程约束(swift)
Posted
技术标签:
【中文标题】屏幕旋转的编程约束(swift)【英文标题】:Programmatic constraints with screen rotation (swift) 【发布时间】:2020-08-11 16:32:59 【问题描述】:我有 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)
我做错了什么???
【问题讨论】:
在更新约束之前,你必须先清除旧的。 你做错了很多事情,但首先......你认为“纵向方向”是什么?如果应用在 iPad 上并且用户使用 SplitView / SlideOver 进行多任务处理,则设备可能处于“横向”方向,但您的应用可能又高又窄。 @DonMag:到目前为止,我只为 iPhone 做这件事,为 iPad 整理想法以备后用……我做错了什么? (我对此很陌生......) @Niraj Solanki:我最初尝试通过设置 cv1.translatesAutoresizingMaskIntoConstraints = true 来做到这一点,然后再次设置为 false - 但这不起作用。您可能有以下解决方案... 【参考方案1】:重要的是要记住,我们应该使用 size 布局,而不是纵向/横向。使用多任务滑动/拆分视图,让您的视图具有与设备本身不同的高宽比并不罕见。
有多种方法可以处理这个问题……这里有一种方法。
创建一个“宽布局”约束数组 创建一个“窄布局”约束数组 创建一组“通用”约束 - 无论是窄“方向”还是宽“方向”,这些约束都适用 根据视图的大小激活/停用宽和窄约束试试这个例子:
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
)
【讨论】:
【参考方案2】:参考:- 更新约束之前检查已应用的约束。如果已经有约束,请获取它们并更新其他约束。
例子:-
//Height
if let heightConstraint = cv1.constraints.first(where: $0.firstAttribute == .height )
heightConstraint.constant = safeAreaHeight
else
NSLayoutConstraint.activate([
cv1.heightAnchor.constraint(equalToConstant: safeAreaHeight)
])
//Width
if let widthConstraint = cv1.constraints.first(where: $0.firstAttribute == .width )
widthConstraint.constant = safeAreaWidth / 2
else
NSLayoutConstraint.activate([
cv1.widthAnchor.constraint(equalToConstant: safeAreaWidth / 2)
])
【讨论】:
以上是关于屏幕旋转的编程约束(swift)的主要内容,如果未能解决你的问题,请参考以下文章