iOS 橫豎屏切換解決方案
前言
在大多數(shù)項目中,App 的 UI 方向都是豎屏的,所以一般會在 target 中將屏幕方向設(shè)置為只支持豎屏。如下圖

而在項目中可能又存在少量的橫屏頁面,或少數(shù)支持多方向的頁面。視頻播放頁,通常都支持自動旋轉(zhuǎn)。在我的項目中就出現(xiàn)這種情況,為了解決這個問題,所以對 UIViewController 進(jìn)行了分類擴(kuò)展。
方案使用
效果

默認(rèn)情況下是豎屏轉(zhuǎn)動手機(jī),不跟隨旋轉(zhuǎn),實現(xiàn)支持方向方法后,可以跟隨。
Demo鏈接地址: 前往
引入
使用 Cocoapods
pod 'AutoRotation'
手動
前往項目地址,下載之后將 AutoRotation 文件夾中的文件拖到項目中即可
配置
將工程設(shè)置為只支持豎屏,如圖

使用
由于項目中絕大多數(shù)頁面都是豎屏的,所以默認(rèn)情況下,所有的 Controller 將是豎屏展示。在需要橫屏或多方向的 Controller 中實現(xiàn)代碼如下:
#import <AutoRotation/AutoRotation.h>
// 或
#import "AutoRotation.h"
// ViewController.m 中實現(xiàn)下面方法
- (UIInterfaceOrientationMask)ar_supportedOrientations {
// 支持三個方法
return UIInterfaceOrientationMaskAllButUpsideDown;
}
通過實現(xiàn)上述方法,即可讓特定的 Controller 支持設(shè)定的方向。
其它 API
/// 轉(zhuǎn)到指定方向
- (void)ar_turnToOrientation:(UIInterfaceOrientationMask)orientation;
/// 轉(zhuǎn)為豎屏
- (void)ar_turnToPortrait;
/// 轉(zhuǎn)為橫屏
- (void)ar_turnToLandscape;
/// 添加特殊處理Controller,用于處理如 AlertContoller 等情況
+ (void)ar_addSpecialControllers:(NSArray<NSString *> *)controllers;
/// 移除特殊處理Controller
+ (void)ar_removeSpecialControllers:(NSArray<NSString *> *)controllers;
解決思路
- 使用 Runtime 交互 Appdelegate 和 UIViewController 的相關(guān)方法
- 獲取當(dāng)前可見的頂部 Controller 的方法
- 將頂部 Controller 支持的方向返回給 Appdelegate 中的
application:supportedInterfaceOrientationsForWindow:方法 - 顯示出對應(yīng)用的方向
實現(xiàn)思路其實比較簡單,當(dāng)然中途會有一些問題,代碼實現(xiàn)中對一些異常情況進(jìn)行了處理。
屏幕方向枚舉
iOS 中關(guān)于方向的枚舉有三種
UIInterfaceOrientationMask
typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
} __TVOS_PROHIBITED;
UIInterfaceOrientation
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
} __TVOS_PROHIBITED;
UIDeviceOrientation
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom
UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top
UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right
UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left
UIDeviceOrientationFaceUp, // Device oriented flat, face up
UIDeviceOrientationFaceDown // Device oriented flat, face down
} __TVOS_PROHIBITED;
手動更改屏幕方向
由于方案中并不更改工程設(shè)置,只勾選了豎屏,所以在實現(xiàn)中使用了手動轉(zhuǎn)屏方法。代碼如下:
+ (BOOL)setOrientation:(UIInterfaceOrientationMask)orientation {
UIInterfaceOrientation interfaceOrientation = [self getOrientationWithOrientationMask:orientation];
UIInterfaceOrientation currentOrientation = [UIViewController currentOrientation];
if (currentOrientation == interfaceOrientation) {
return NO;
}
NSLog(@"ar log: interfaceOrientation:%ld", interfaceOrientation);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[[UIApplication sharedApplication] setStatusBarOrientation:interfaceOrientation animated:NO];
#pragma clang diagnostic pop
[[UIDevice currentDevice] setValue:@(interfaceOrientation) forKey:@"orientation"];
return YES;
}
實現(xiàn)
通過上述的基礎(chǔ)及實現(xiàn)思路鋪墊,總體實現(xiàn)起來相對簡單。下面是碰到的一些問題。
topViewController 不是在 keyWindow 上
解決辦法是獲取 Appdelegate 中的 window 的 topViewController
AlertView 和 AlertController
解決辦法是 hook dismissViewControllerAnimated 方法并且將相關(guān)的類名加入到特殊 Controller 列表中,在處理這些特殊 Controller 時,將獲取前一個控制器的方向
具體實現(xiàn)代碼可前往 Git 上查看