首先,先熟悉下2个枚举
UIDeviceOrientation:设备方向
public enum UIDeviceOrientation : Int {
case unknown
case portrait // Device oriented vertically, home button on the bottom
case portraitUpsideDown // Device oriented vertically, home button on the top
case landscapeLeft // Device oriented horizontally, home button on the right
case landscapeRight // Device oriented horizontally, home button on the left
case faceUp // Device oriented flat, face up
case faceDown // Device oriented flat, face down
}
这个枚举值用来表示物理设备的方向,只能读取,不能设置,我们可以在代码中监控这个方向的变化
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// 添加通知前务必写下面的代码,否则通知不会有效
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(showDeviceOrientation), name: UIDevice.orientationDidChangeNotification, object: nil)
return true
}
@objc func showDeviceOrientation() {
switch UIDevice.current.orientation {
case .portrait:
print("portrait")
case .landscapeLeft:
print("landscapeLeft")
case .landscapeRight:
print("landscapeRight")
case .portraitUpsideDown:
print("portraitUpsideDown")
default:
print("unknow")
}
}
UIInterfaceOrientationMask:页面方向
public struct UIInterfaceOrientationMask : OptionSet {
public init(rawValue: UInt)
public static var portrait: UIInterfaceOrientationMask { get }
public static var landscapeLeft: UIInterfaceOrientationMask { get }
public static var landscapeRight: UIInterfaceOrientationMask { get }
public static var portraitUpsideDown: UIInterfaceOrientationMask { get }
public static var landscape: UIInterfaceOrientationMask { get }
public static var all: UIInterfaceOrientationMask { get }
public static var allButUpsideDown: UIInterfaceOrientationMask { get }
}
用来表示具体某个页面的方向。对单独页面设置的时候,最主要的就是对这个枚举进行设置
App整体方向设置
项目中,设置App支持的显示方向的地方有2处。
-
在Xcode中进行设置
xcode设置方向.png
2.在代码中进行全局设置.需要注意的是,代码中的设置会覆盖xcode中的设置
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return .all
}
视图控制器设置方向的优先级
在对整体项目进行设置后,我们需要对具体的视图控制器进行设置。通常来讲,大多数项目的视图控制器结构如下
如图,TabbarController的旋转控制优先级最高,然后是NavigationController,最后是ViewController。
也就是说,如果TabbarController关闭了旋转控制,那么整个App都不能对旋转作出响应。
整个项目默认只支持竖屏,部分页面支持横屏
根据以上的介绍,现在来实现这个需求。
-
首先,需要全局设置项目支持的方向,可以在xcode中设置,也可以通过代码设置,这里我们直接在xcode中设置。
全局设置.png
这里我们设置支持竖屏和横屏,此时,在项目中未编写任何屏幕旋转相关的代码,如果手机没有开启旋转锁定的话,App中的页面默认会根据屏幕旋转方向进行旋转。但是因为这里我们设置的是只支持竖屏,所以所有页面只会竖屏显示。
为了让某个页面会跟随设备方向旋转,我们需要重写和页面方向相关的属性,主要是下面2个
// 是否支持屏幕旋转
override var shouldAutorotate: Bool {
get {
}
}
// 当前页面支持的显示方向
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
}
}
如之前所说,页面旋转的设置是有优先级的,高优先级的会控制低优先级的,所以,我们需要从跟控制器开始设置,这里先为UINavigationController和UITabBarController写2个extension。
这2个extension的主要作用是让UINavigationController和UITabBarController能够让顶层控制器的选择设置对跟控制器的设置进行重写,并且能够依次传递给底层的控制器。
extension UINavigationController {
open override var shouldAutorotate: Bool {
get{
return self.topViewController!.shouldAutorotate
}
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
return topViewController!.supportedInterfaceOrientations
}
}
}
extension UITabBarController {
open override var shouldAutorotate: Bool {
get{
return selectedViewController!.shouldAutorotate
}
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
return selectedViewController!.supportedInterfaceOrientations
}
}
}
由于我们希望整个项目只支持竖屏,因此考虑可以自定义一个父UIViewController,所有的UIViewController都继承它。并且在这个父控制器中,设置不支持旋转,只支持竖屏。
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// 不根据设备进行旋转
override var shouldAutorotate: Bool {
get {
return false
}
}
// 默认只支持竖屏显示
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
return .portrait
}
}
}
父控制器设置完后,我们就可以在需要支持旋转的子控制器中重写这些设置,用来支持旋转,比如我们想某个页面可以支持旋转,并且支持所有方向,那么我们就可以如下设置
class TestViewControllerA: MyViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// 支持旋转
override var shouldAutorotate: Bool {
get {
return true
}
}
// 支持所有方向
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
return .all
}
}
}
这样设置后,在该页面,如果设备的旋转锁定打开,则该页面会根据设备方向进行旋转。
问题是,该页面进来的时候依旧默认是竖屏显示,为了实现页面刚进来时就是横屏,我们需要重写对应的方法。
在该页面新增一个按钮,点击进入一个新页面,新页面默认横屏显示
@IBAction func defaultLandscape(_ sender: Any) {
let tb = TestViewControllerB.init()
navigationController?.pushViewController(tb, animated: true)
}
TestViewControllerB中代码如下
class TestViewControllerB: MyViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// 刚进来时强制横屏
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let value = UIInterfaceOrientation.landscapeLeft.rawValue
if UIDevice.current.responds(to: #selector(setValue(_:forKey:))) {
UIDevice.current.setValue(value, forKey: "orientation")
}
}
// 退出时设置设备方向为默认方向
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let value = UIInterfaceOrientation.portrait.rawValue
if UIDevice.current.responds(to: #selector(setValue(_:forKey:))) {
UIDevice.current.setValue(value, forKey: "orientation")
}
}
override var shouldAutorotate: Bool {
get {
return true
}
}
//默认支持的方向
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
get {
return .landscapeLeft
}
}
}
这样就实现了前面说的所有页面默认竖屏,部分页面横屏显示的需求。