首先,您需要通过代码实现此操作。您必须首先对动态约束进行IBoutlet。
恒定约束://这些约束在两个方向上都保持不变
1- RedView顶部间距至SuperView
2- RedView右侧间距至SuperView
3- BlueView左侧间距至SuperView
4- BlueView底部间距至Superview
动态约束
竖屏约束:
1- RedView高度
2- RedView垂直间距至BlueView
3- RedView左侧间距至SuperView
4- BlueView右侧间距至SuperView
横屏约束:
1- RedView宽度
2- RedView水平间距至BlueView
3- RedView底部间距至SuperView
4- BlueView顶部间距至SuperView
现在,您需要覆盖在方向更改时调用的方法。
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animateAlongsideTransition({ (UIViewControllerTransitionCoordinatorContext) -> Void in
let orient = UIApplication.sharedApplication().statusBarOrientation
switch orient {
case .Portrait:
print("Portrait")
self.ApplyportraitConstraint()
break
// Do something
default:
print("LandScape")
// Do something else
self.applyLandScapeConstraint()
break
}
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
print("rotation completed")
})
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
}
调用这两个函数
纵向方向函数
func ApplyportraitConstraint(){
self.view.addConstraint(self.RedViewHeight)
self.view.addConstraint(self.RedView_VerticalSpace_To_BlueView)
self.view.addConstraint(self.RedView_LeadingSpace_To_SuperView)
self.view.addConstraint(self.BlueView_TrailingSpace_To_SuperView)
self.view.removeConstraint(self.RedViewWidth)
self.view.removeConstraint(self.RedView_HorizontalSpace_To_BlueView)
self.view.removeConstraint(self.RedView_BottomSpace_To_SuperView)
self.view.removeConstraint(self.BlueView_TopSpace_To_SuperView)
}
景观方向功能
func applyLandScapeConstraint(){
self.view.removeConstraint(self.RedViewHeight)
self.view.removeConstraint(self.RedView_VerticalSpace_To_BlueView)
self.view.removeConstraint(self.RedView_LeadingSpace_To_SuperView)
self.view.removeConstraint(self.BlueView_TrailingSpace_To_SuperView)
self.view.addConstraint(self.RedViewWidth)
self.view.addConstraint(self.RedView_HorizontalSpace_To_BlueView)
self.view.addConstraint(self.RedView_BottomSpace_To_SuperView)
self.view.addConstraint(self.BlueView_TopSpace_To_SuperView)
}
希望通过编码的布局管理帮助您理解它。 如果您还无法理解,请在
Github 上检查我的代码:
如果您收到了警告,请将高度和宽度的约束优先级设置为999。
iPad在横屏模式下没有大小类。我认为原因是因为大多数情况下都不需要。但是可以像被接受的答案一样,在设备方向改变时激活和停用约束。
对iPhone用户来说,以下内容可能会有所帮助。
是的,利用自动布局和大小类可以在界面构建器中实现此功能。您不需要编写代码。
首先,选择wAny hAny大小类。
这里是如何选择大小类。
在视图控制器中添加两个视图。红色视图在上方,蓝色视图在下方。就像您的第一张图片一样。
红色视图的约束条件为:
蓝色视图的约束条件为:
这就是所有为肖像模式设置的内容。
现在,将大小类更改为wAny hCompact(第一行中的前两列)。这个类用于iPhone横屏模式。
现在,您必须使用安装和卸载的概念。
将更改红色视图的约束条件:
这将使红色视图位于右侧并具有50的宽度。
现在蓝色视图的约束发生了变化:
添加两个新约束:
这将把红色视图附加在蓝色视图左侧。
希望对您有所帮助。
我使用Obj-C实现了这个功能,并在我的github上发布了代码。解决方案涉及少量代码,大部分工作集中在自动布局和命名规范上…… README文件详细说明了我的做法。我在ViewController上使用的代码基本上是这个方法:
- (void)setUpViewConstraintsForInterfaceOrientation:(InterfaceOrientation)interfaceOrientation {
self.lastOrientation = interfaceOrientation;
if (interfaceOrientation == Landscape) {
[NSLayoutConstraint deactivateConstraints:self.portraitConstraintsCollection];
[NSLayoutConstraint activateConstraints:self.landscapeConstraintsCollection];
} else if(interfaceOrientation == Portrait){
[NSLayoutConstraint deactivateConstraints:self.landscapeConstraintsCollection];
[NSLayoutConstraint activateConstraints:self.portraitConstraintsCollection];
}
[self.view layoutIfNeeded];
}
portraitConstraintsCollection和landscapeConstraintsCollection是IBOutletCollection属性,用于管理特定方向的约束条件。
而自动布局解决方案只需要安装和卸载约束条件(激活和停用),不需要添加或删除约束条件。
我的任务基本上是相似的。我需要为iPhone和iPad提供肖像和横向的限制条件。此外,黄色和灰色视图的位置应该是基本相同的,但是在iPhone横向和iPad横向上,黄色视图的宽度应该不同(在iPhone上为屏幕的40%,在iPad上为屏幕的60%):
我使用特征集合来设置iPhone方向的限制条件,并定义每个限制条件要安装在哪个集合中。对于iPhone而言,有wChR(肖像)和wChC(横向)。或者是wC和hAny:
但是对于iPad上的纵向和横向方向,只使用单个特征集合(wRhR),因此iPhone使用的方式不适用。为了更改这些情况下的约束条件,我创建了两个约束集(第一个用于iPad横向,第二个用于iPad纵向):
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *ipadLandscapeConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *ipadPortraitConstraints;
configConstraints
方法,根据iPad的方向切换约束集,并重写了viewWillTransitionToSize
方法:- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[self configConstraints];
}
- (void)configConstraints {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// iPad landscape orientation
if (UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
[NSLayoutConstraint deactivateConstraints:self.ipadPortraitConstraints];
[NSLayoutConstraint activateConstraints:self.ipadLandscapeConstraints];
}
// iPad portrait orientation
else {
[NSLayoutConstraint deactivateConstraints:self.ipadLandscapeConstraints];
[NSLayoutConstraint activateConstraints:self.ipadPortraitConstraints];
}
[self.view layoutIfNeeded];
}
}
configConstraints
方法,但基本思想已经描述完毕。使用自动布局或少量代码可以完成吗?
对于iPad,您需要两者都使用。
在您的视图控制器中实现UIContentContainer协议。
viewWillTransitionToSize(_ size: CGSize,withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
讨论 UIKit在改变所呈现视图控制器的视图大小之前调用此方法。您可以在自己的对象中覆盖此方法,并使用它来执行与大小更改相关的其他任务。例如,容器视图控制器可能会使用此方法来覆盖其嵌入式子视图控制器的特征。使用提供的协调器对象来动画显示所做的任何更改。
如果您在自定义视图控制器中覆盖此方法,请始终在您的实现中某个位置调用super,以便UIKit可以适当地转发大小更改消息。视图控制器将大小更改消息转发到其视图和子视图控制器。呈现控制器将大小更改转发给其呈现的视图控制器。
是您需要实现的方法。在此方法中,您需要检查大小的属性宽度和高度,以确定布局应如何更改-即横向或纵向布局。请注意,此方法表示它将更改为传入的大小。
将这两个视图放入堆栈视图中,通过更改堆栈视图的轴方向来实现更简单的操作。
我唯一轻松实现它在iPad和iPhone上工作的方法是通过编程方式。这是使用Swift 5.0在Xcode 10.2中完成的。
在您的ViewController中,定义您想根据方向更改的两个视图:
@IBOutlet weak var raceInfoView: UIStackView!
@IBOutlet weak var raceListView: UITableView!
然后在你的故事板中定义那些始终保持不变的约束条件,并定义那些在你的ViewController中将要更改的约束条件。
private var portraitRaceInfoViewTrailing: NSLayoutConstraint!
private var portraitRaceInfoViewBottom: NSLayoutConstraint!
private var portraitRaceListViewLeading: NSLayoutConstraint!
private var landscapeRaceInfoViewTrailing: NSLayoutConstraint!
private var landscapeRaceInfoViewBottom: NSLayoutConstraint!
private var landscapeRaceListViewTop: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
portraitRaceInfoViewTrailing = NSLayoutConstraint(
item: racesView as Any, attribute: NSLayoutConstraint.Attribute.trailing,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: raceInfoView, attribute: NSLayoutConstraint.Attribute.trailing,
multiplier: 1, constant: 0)
portraitRaceInfoViewBottom = NSLayoutConstraint(
item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.top,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: raceInfoView, attribute: NSLayoutConstraint.Attribute.bottom,
multiplier: 1, constant: 0)
portraitRaceListViewLeading = NSLayoutConstraint(
item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.leading,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: racesView, attribute: NSLayoutConstraint.Attribute.leading,
multiplier: 1, constant: 0)
landscapeRaceInfoViewTrailing = NSLayoutConstraint(
item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.leading,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: raceInfoView, attribute: NSLayoutConstraint.Attribute.trailing,
multiplier: 1, constant: 0)
landscapeRaceInfoViewBottom = NSLayoutConstraint(
item: raceInfoView as Any, attribute: NSLayoutConstraint.Attribute.bottom,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: racesView, attribute: NSLayoutConstraint.Attribute.bottom,
multiplier: 1, constant: 0)
landscapeRaceListViewTop = NSLayoutConstraint(
item: raceListView as Any, attribute: NSLayoutConstraint.Attribute.top,
relatedBy: NSLayoutConstraint.Relation.equal,
toItem: racesView, attribute: NSLayoutConstraint.Attribute.top,
multiplier: 1, constant: 0)
applyOrientationConstraints()
}
在程序中编程声明约束看起来有点可怕,但实际上非常容易。您可以在故事板中创建约束并查看所有值,然后将正确的值复制到代码中的正确位置。
最后,在viewDidLoad
中使用applyOrientationConstraints()
应用约束。
func applyOrientationConstraints() {
let orient = UIApplication.shared.statusBarOrientation
switch orient {
case .portrait:
NSLayoutConstraint.activate([portraitRaceInfoViewTrailing, portraitRaceInfoViewBottom, portraitRaceListViewLeading])
NSLayoutConstraint.deactivate([landscapeRaceInfoViewTrailing, landscapeRaceInfoViewBottom, landscapeRaceListViewTop])
break
default:
NSLayoutConstraint.deactivate([portraitRaceInfoViewTrailing, portraitRaceInfoViewBottom, portraitRaceListViewLeading])
NSLayoutConstraint.activate([landscapeRaceInfoViewTrailing, landscapeRaceInfoViewBottom, landscapeRaceListViewTop])
break
}
}
viewWillTransition
方法,在方向改变时应用约束。override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
self.applyOrientationConstraints()
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
print("rotation completed")
})
super.viewWillTransition(to: size, with: coordinator)
}
个人意见.. Swift 5:
(请连接插座....)
//
// ViewController.swift
// AutoLayoutSampleOnRotation
//
// Created by ing.conti on 13/09/2019.
// Copyright © 2019 ing.conti. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var redView: UIView!
@IBOutlet weak var yellowView: UIView!
private var red_TopPortrait : NSLayoutConstraint?
private var red_TopLandscape : NSLayoutConstraint?
private var red_LeftPortrait : NSLayoutConstraint?
private var red_LeftLandscape : NSLayoutConstraint?
private var red_RightPortrait : NSLayoutConstraint?
private var red_RightLandscape : NSLayoutConstraint?
private var red_BottomPortrait : NSLayoutConstraint?
private var red_BottomLandscape : NSLayoutConstraint?
private var red_HeightPortrait : NSLayoutConstraint?
private var red_WidthLandscape : NSLayoutConstraint?
///
private var yellow_TopPortrait : NSLayoutConstraint?
private var yellow_TopLandscape : NSLayoutConstraint?
private var yellow_LeftPortrait : NSLayoutConstraint?
private var yellow_LeftLandscape : NSLayoutConstraint?
private var yellow_RightPortrait : NSLayoutConstraint?
private var yellow_RightLandscape : NSLayoutConstraint?
private var yellow_BottomPortrait : NSLayoutConstraint?
private var yellow_BottomLandscape : NSLayoutConstraint?
private let H_SpaceBetween = CGFloat(20)
private let V_SpaceBetween = CGFloat(50)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
redView.translatesAutoresizingMaskIntoConstraints = false
yellowView.translatesAutoresizingMaskIntoConstraints = false
buildConstraintsForRed()
buildConstraintsForYellow()
applyConstraints()
}
final private func buildConstraintsForRed(){
let portraitTopMargin = CGFloat(70)
let portraitLeftMargin = CGFloat(70)
let portraitRightMargin = CGFloat(70)
let landscapeTopMargin = CGFloat(70)
let landscapeLeftMargin = CGFloat(70)
let landscapeBottomMargin = CGFloat(70)
// TOP P
red_TopPortrait = NSLayoutConstraint(item: redView as Any,
attribute: .top,
relatedBy: .equal,
toItem: self.view,
attribute: .top,
multiplier: 1,
constant: portraitTopMargin)
red_TopPortrait!.identifier = "red_TopPortrait"
// LEFT-Heading P
red_LeftPortrait = NSLayoutConstraint(item: redView as Any,
attribute: .leading,
relatedBy: .equal,
toItem: self.view,
attribute: .leading,
multiplier: 1,
constant: portraitLeftMargin)
red_LeftPortrait!.identifier = "red_LeftPortrait"
// RIGHT - trailing P
red_RightPortrait = NSLayoutConstraint(item: redView as Any,
attribute: .trailing,
relatedBy: .equal,
toItem: self.view,
attribute: .trailing,
multiplier: 1,
constant: -portraitRightMargin)
red_RightPortrait!.identifier = "red_RightPortrait"
// BOTTOM: P
red_BottomPortrait = NSLayoutConstraint(item: redView as Any,
attribute: .bottom,
relatedBy: .equal,
toItem: yellowView,
attribute: .top,
multiplier: 1,
constant: -V_SpaceBetween)
red_BottomPortrait!.identifier = "red_BottomPortrait"
// HEIGHT: P
red_HeightPortrait = NSLayoutConstraint(item: redView as Any,
attribute: .height,
relatedBy: .equal,
toItem: self.view,
attribute: .height,
multiplier: 0.3,
constant: 0)
red_HeightPortrait?.identifier = "red_HeightPortrait"
//LANDSCAPE
// TOP L
red_TopLandscape = NSLayoutConstraint(item: redView as Any,
attribute: .top,
relatedBy: .equal,
toItem: self.view,
attribute: .top,
multiplier: 1,
constant: landscapeTopMargin)
red_TopLandscape!.identifier = "red_TopLandscape"
// LEFT-Heading L
red_LeftLandscape = NSLayoutConstraint(item: redView as Any,
attribute: .leading,
relatedBy: .equal,
toItem: self.view,
attribute: .leading,
multiplier: 1,
constant: landscapeLeftMargin)
red_LeftLandscape!.identifier = "red_LeftLandscape"
// RIGHT - trailing L
red_RightLandscape = NSLayoutConstraint(item: redView as Any,
attribute: .trailing,
relatedBy: .equal,
toItem: yellowView,
attribute: .leading,
multiplier: 1,
constant: -H_SpaceBetween)
red_RightLandscape!.identifier = "red_RightLandscape"
// BOTTOM: L
red_BottomLandscape = NSLayoutConstraint(item: redView as Any,
attribute: .bottom,
relatedBy: .equal,
toItem: self.view,
attribute: .bottom,
multiplier: 1,
constant: -landscapeBottomMargin)
red_BottomLandscape?.identifier = "red_BottomLandscape"
// Width L:
red_WidthLandscape = NSLayoutConstraint(item: redView as Any,
attribute: .width,
relatedBy: .equal,
toItem: self.view,
attribute: .width,
multiplier: 0.3,
constant: 0)
red_WidthLandscape!.identifier = "red_WidthLandscape"
}
final private func buildConstraintsForYellow(){
let portraitLeftMargin = CGFloat(20)
let portraitRightMargin = CGFloat(20)
//let portraitHorizMargin = CGFloat(100)
let portraitBottomMargin = CGFloat(20)
let landscaspeTopMargin = CGFloat(20)
let landscaspeRightMargin = CGFloat(20)
let landscapeBottomMargin = CGFloat(20)
// TOP P
yellow_TopPortrait = NSLayoutConstraint(item: yellowView as Any,
attribute: .top,
relatedBy: .equal,
toItem: redView,
attribute: .bottom,
multiplier: 1,
constant: V_SpaceBetween)
yellow_TopPortrait!.identifier = "yellow_TopPortrait"
// LEFT-Heading P
yellow_LeftPortrait = NSLayoutConstraint(item: yellowView as Any,
attribute: .leading,
relatedBy: .equal,
toItem: self.view,
attribute: .leading,
multiplier: 1,
constant: portraitLeftMargin)
yellow_LeftPortrait!.identifier = "yellow_LeftPortrait"
// RIGHT - trailing P
yellow_RightPortrait = NSLayoutConstraint(item: yellowView as Any,
attribute: .trailing,
relatedBy: .equal,
toItem: self.view,
attribute: .trailing,
multiplier: 1,
constant: -portraitRightMargin)
yellow_RightPortrait!.identifier = "yellow_RightPortrait"
// BOTTOM: P
yellow_BottomPortrait = NSLayoutConstraint(item: yellowView as Any,
attribute: .bottom,
relatedBy: .equal,
toItem: self.view,
attribute: .bottom,
multiplier: 1,
constant: -portraitBottomMargin)
yellow_BottomPortrait!.identifier = "yellow_BottomPortrait"
//LANDSSCAPE
// TOP L
yellow_TopLandscape = NSLayoutConstraint(item: yellowView as Any,
attribute: .top,
relatedBy: .equal,
toItem: self.view,
attribute: .top,
multiplier: 1,
constant: landscaspeTopMargin)
yellow_TopLandscape!.identifier = "yellow_TopLandscape"
// LEFT-Heading L
yellow_LeftLandscape = NSLayoutConstraint(item: yellowView as Any,
attribute: .leading,
relatedBy: .equal,
toItem: self.redView,
attribute: .trailing,
multiplier: 1,
constant: H_SpaceBetween)
yellow_LeftLandscape!.identifier = "yellow_LeftLandscape"
// RIGHT - trailing L
yellow_RightLandscape = NSLayoutConstraint(item: yellowView as Any,
attribute: .trailing,
relatedBy: .equal,
toItem: self.view,
attribute: .trailing,
multiplier: 1,
constant: -landscaspeRightMargin)
yellow_RightLandscape!.identifier = "yellow_RightLandscape"
// BOTTOM: L
yellow_BottomLandscape = NSLayoutConstraint(item: yellowView as Any,
attribute: .bottom,
relatedBy: .equal,
toItem: self.view,
attribute: .bottom,
multiplier: 1,
constant: -landscapeBottomMargin)
yellow_BottomLandscape!.identifier = "yellow_BottomLandscape"
}
final private func removeRedConstraints() {
if let c = red_LeftPortrait {self.view.removeConstraint(c)}
if let c = red_LeftLandscape {self.view.removeConstraint(c)}
if let c = red_RightPortrait {self.view.removeConstraint(c)}
if let c = red_RightLandscape {self.view.removeConstraint(c)}
if let c = red_TopPortrait {self.view.removeConstraint(c)}
if let c = red_TopLandscape {self.view.removeConstraint(c)}
if let c = red_BottomPortrait {self.view.removeConstraint(c)}
if let c = red_BottomLandscape {self.view.removeConstraint(c)}
if let c = red_HeightPortrait {self.view.removeConstraint(c)}
if let c = red_WidthLandscape {self.view.removeConstraint(c)}
}
final private func removeYellowConstraints() {
if let c = yellow_LeftPortrait {self.view.removeConstraint(c)}
if let c = yellow_LeftLandscape {self.view.removeConstraint(c)}
if let c = yellow_RightPortrait {self.view.removeConstraint(c)}
if let c = yellow_RightLandscape {self.view.removeConstraint(c)}
if let c = yellow_TopPortrait {self.view.removeConstraint(c)}
if let c = yellow_TopLandscape {self.view.removeConstraint(c)}
if let c = yellow_BottomPortrait {self.view.removeConstraint(c)}
if let c = yellow_BottomLandscape {self.view.removeConstraint(c)}
}
final private func applyPortraitConstraint(){
removeRedConstraints()
removeYellowConstraints()
self.view.addConstraint(self.red_LeftPortrait!)
self.view.addConstraint(self.red_RightPortrait!)
self.view.addConstraint(self.red_TopPortrait!)
self.view.addConstraint(self.red_BottomPortrait!)
self.view.addConstraint(self.red_HeightPortrait!)
self.view.addConstraint(self.yellow_LeftPortrait!)
self.view.addConstraint(self.yellow_RightPortrait!)
self.view.addConstraint(self.yellow_TopPortrait!)
self.view.addConstraint(self.yellow_BottomPortrait!)
}
final private func applyLandscapeConstraint(){
removeRedConstraints()
removeYellowConstraints()
self.view.addConstraint(self.red_LeftLandscape!)
self.view.addConstraint(self.red_RightLandscape!)
self.view.addConstraint(self.red_TopLandscape!)
self.view.addConstraint(self.red_BottomLandscape!)
self.view.addConstraint(self.red_WidthLandscape!)
self.view.addConstraint(self.yellow_LeftLandscape!)
self.view.addConstraint(self.yellow_RightLandscape!)
self.view.addConstraint(self.yellow_TopLandscape!)
self.view.addConstraint(self.yellow_BottomLandscape!)
}
final private func applyConstraints(){
let orient = UIApplication.shared.statusBarOrientation
switch orient {
case .portrait:
print("Portrait")
self.applyPortraitConstraint()
break
// Do something
default:
print("LandScape")
// Do something else
self.applyLandscapeConstraint()
break
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
self.applyConstraints()
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
print("rotation completed")
})
super.viewWillTransition(to: size, with: coordinator)
}
}