iOS 横竖屏切换解决方案

iOS 横竖屏切换解决方案

前言

在大多数项目中,App 的 UI 方向都是竖屏的,所以一般会在 target 中将屏幕方向设置为只支持竖屏。如下图

image

而在项目中可能又存在少量的横屏页面,或少数支持多方向的页面。视频播放页,通常都支持自动旋转。在我的项目中就出现这种情况,为了解决这个问题,所以对 UIViewController 进行了分类扩展。

方案使用

效果

image

默认情况下是竖屏转动手机,不跟随旋转,实现支持方向方法后,可以跟随。

Demo链接地址: 前往

引入

使用 Cocoapods

pod 'AutoRotation'

手动

前往项目地址,下载之后将 AutoRotation 文件夹中的文件拖到项目中即可

配置

将工程设置为只支持竖屏,如图

image

使用

由于项目中绝大多数页面都是竖屏的,所以默认情况下,所有的 Controller 将是竖屏展示。在需要横屏或多方向的 Controller 中实现代码如下:

#import 
// 或
#import "AutoRotation.h"

// ViewController.m 中实现下面方法
- (UIInterfaceOrientationMask)ar_supportedOrientations {
    // 支持三个方法
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

通过实现上述方法,即可让特定的 Controller 支持设定的方向。

其它 API

/// 转到指定方向
- (void)ar_turnToOrientation:(UIInterfaceOrientationMask)orientation;
/// 转为竖屏
- (void)ar_turnToPortrait;
/// 转为横屏
- (void)ar_turnToLandscape;
/// 添加特殊处理Controller,用于处理如 AlertContoller 等情况
+ (void)ar_addSpecialControllers:(NSArray *)controllers;
/// 移除特殊处理Controller
+ (void)ar_removeSpecialControllers:(NSArray *)controllers;

解决思路

  1. 使用 Runtime 交互 Appdelegate 和 UIViewController 的相关方法
  2. 获取当前可见的顶部 Controller 的方法
  3. 将顶部 Controller 支持的方向返回给 Appdelegate 中的 application:supportedInterfaceOrientationsForWindow:方法
  4. 显示出对应用的方向

实现思路其实比较简单,当然中途会有一些问题,代码实现中对一些异常情况进行了处理。

屏幕方向枚举

iOS 中关于方向的枚举有三种

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;

手动更改屏幕方向

由于方案中并不更改工程设置,只勾选了竖屏,所以在实现中使用了手动转屏方法。代码如下:

+ (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;
}

实现

通过上述的基础及实现思路铺垫,总体实现起来相对简单。下面是碰到的一些问题。

topViewController 不是在 keyWindow 上

解决办法是获取 Appdelegate 中的 window 的 topViewController

AlertView 和 AlertController

解决办法是 hook dismissViewControllerAnimated 方法并且将相关的类名加入到特殊 Controller 列表中,在处理这些特殊 Controller 时,将获取前一个控制器的方向

具体实现代码可前往 Git 上查看

你可能感兴趣的:(iOS 横竖屏切换解决方案)