基础概念

UIDeviceOrientation

UIDeviceOrientation,表明设备朝向,能够经过[UIDevice currentDevice] orientation]获取,取值有:

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,             // 不知道,发动时会出现
    UIDeviceOrientationPortrait,            // 竖屏,home键在底部
    UIDeviceOrientationPortraitUpsideDown,  // 倒立,home键在顶部
    UIDeviceOrientationLandscapeLeft,       // 左横屏,home键在右边
    UIDeviceOrientationLandscapeRight,      // 右横屏,home键在左面
    UIDeviceOrientationFaceUp,              // 屏幕朝上
    UIDeviceOrientationFaceDown             // 屏幕朝下
}

UIInterfaceOrientation

UIInterfaceOrientation,表明页面内容朝向,留意UIInterfaceOrientation和UIDeviceOrientation的联络,其中UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,这是由于: This is because rotating the device to the left requires rotating the content to the right. 不用特别细究两者之间联络,咱们只需求依据需求设置好UIInterfaceOrientation即可,经过 [UIApplication shareApplication] statusBarOrientation]能够获取当时状态栏朝向。

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

UIInterfaceOrientationMask

UIInterfaceOrientationMask,是由页面内容朝向的二进制偏移组成,用来更方便描绘某个界面支撑的朝向。比方说下面的UIInterfaceOrientationMaskLandscape,其实便是由MaskLandscapeLeft和MaskLandscapeRight组成,这样能够方便描绘设备支撑两个横屏方向。

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),
};

UIViewController相关

UIViewController关于反正屏的三个办法:

  1. shouldAutorotate,页面是否答应主动旋转,被弃用api:-shouldAutorotateToInterfaceOrientation的取代者;默认值为YES,表明当时界面答应跟随设备旋转而主动旋转;
  2. supportedInterfaceOrientations,该界面支撑的界面朝向,能够回来四个朝向的恣意组合,iPad默认值是四个朝向都支撑,iPhone默认值是除了UpsideDown的三个朝向。这个办法回调的条件是shouldAutorotate=YES。
  3. preferredInterfaceOrientationForPresentation,该界面被present出来的界面朝向,能够回来四个朝向的恣意组合。假定没有回来,则present时和本来界面坚持一致。

AppDelegate相关

AppDelegate的supportedInterfaceOrientationsForWindow办法,依据需求回来当时window是否支撑横屏。

- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window;

工程装备相关

在xcode的工程设置的General能够装备iPhone和iPad的页面朝向支撑。

iOS横竖屏切换

反正屏切换实例

竖屏界面怎么present横屏界面

竖屏present横屏是很遍及的场景,比方说视频播映场景的全屏切换,就能够在当时竖屏的界面present一个横屏播映界面的办法,完成反正屏切换。具体的操作过程只需求两步:

1,设置modalPresentationStyle为UIModalPresentationFullScreen; 2、preferredInterfaceOrientationForPresentation办法,回来UIInterfaceOrientationLandscapeRight;

比方说下面的LandscapeViewController界面:

// 点击时设置
- (void)onClick {
    LandscapeViewController *landVC = [[LandscapeViewController alloc] init];
    landVC.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:landVC animated:YES completion:nil];
}
// LandscapeViewController内部代码
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

思考:

1、假定是横屏转竖屏呢? 2、假定想要自定义旋转作用完成呢?(UIViewControllerAnimatedTransitioning协议)

竖屏界面怎么push横屏界面

比方说这样的场景:App的rootVC是navigationVC,导航栈内先有一个竖屏界面,现在想要push一个横屏界面LandscapeViewController。

一个简单的办法如下:

// appdelegate完成
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    if ([self.navigationVC.topViewController isKindOfClass:LandscapeViewController.class]) {
        return UIInterfaceOrientationMaskLandscapeRight;
    }
    else {
        return UIInterfaceOrientationMaskPortrait;
    }
}
// LandscapeViewController内部完成
- (void)viewDidLoad {
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:@selector(setOrientation:)]];
    invocation.selector = NSSelectorFromString(@"setOrientation:");
    invocation.target = [UIDevice currentDevice];
    int initOrientation = UIDeviceOrientationLandscapeRight;
    [invocation setArgument:&initOrientation atIndex:2];
    [invocation invoke];
}

思考:

1、这儿为什么没有用到UIViewController的三个办法?
2、在viewDidLoad调用的旋转办法是什么意思?

横屏竖切换机制剖析

前面的实例介绍了怎么支撑切换,可是也产生一些疑问:
工程装备文件也没有设置横屏,为什么后边就能支撑横屏?
工程装备、AppDelegate、UIViewController这三者,在反正屏切换过程的联络是什么?
主动旋转和手动旋转有什么区别?
….
只是知道切换适配代码,是无法构成反正屏切换了解,也就很难答复上述的问题。
由于没有找到解说反正屏切换机制的官方文档,以下依据自己的经历对这个切换的机制进行剖析。

体系怎么知道App对界面朝向的支撑

这儿分两种状况,App发动前和App运行时。

App发动前 在App发动前进程还未加载,代码无法运行,体系必定无法经过AppDelegate或者UIViewController这种代码的办法获取反正屏的装备。所以在这种状况下,工程装备中的plist描绘App对屏幕的适配,就能够很好帮助体系辨认应该以什么样的朝向发动App。
所以在plist中增加横屏的支撑,优点是开屏能够支撑横屏,这样界面展示愈加顺滑;坏处也是开屏支撑了横屏,导致开屏为横屏发动的时分,UIScreen的mainScreen是横屏的巨细,但许多业务逻辑代码都会以[UIScreen mainScreen]去取屏幕宽度和高度,所以很简单取到过错的值。

App运行时 当App进程加载完成,此刻体系能够经过运行时询问的办法,来动态获取不同机遇的界面朝向。
此刻AppDelegate操控的是UIWindow层面的朝向,UIViewController操控的是VC层面的朝向。需求留意的是,当咱们回来UIViewController的朝向时,还要考虑父容器的朝向。一般一个App的界面层级是UIWindow=>RootViewController(容器vc)=>UIViewController(界面vc)。假定只在UIWindow回来界面朝向也是答应的,就如同上面的实例剖析中的push横屏。

不同界面的朝向操控

还是假定UIWindow=>RootViewController(容器vc)=>UIViewController(界面vc)的层级,且当时ViewController是竖屏vc,现在需求push一个横屏界面LandscapeViewController。 在每次界面切换的时分,体系都会回调确认新的界面朝向,最终成果为UIWindow朝向、容器vc朝向、界面vc朝向三者的“与”值。那么假定这个值抵触了呢? 假定supportedInterfaceOrientationsForWindow一向回来的竖屏,那么后边VC设置横屏不会生效; 类似,假定UIWindow设置的是横屏,那么后边VC设置竖屏也不会生效; 假定在界面切换的过程中发现回来的朝向值未确定,体系更倾向于坚持当时朝向不变,并且可能会遇到以下的crash。

Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [LandscapeViewController shouldAutorotate] is returning YES'

这个原则相同适用于当回来多个成果,比方说当时界面是竖屏,然后UIWindow和ViewController的界面朝向都支撑横屏和竖屏都支撑,那么此刻会坚持竖屏。

一种比较常用的设计:

// AppDelegate
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return self.navigationVC.topViewController.supportedInterfaceOrientations;
}
// NavigationController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return self.topViewController.supportedInterfaceOrientations;
}
// LandscapeViewController
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

主动旋转和手动旋转

主动旋转指的是咱们旋转物理设备时,体系会触发界面的旋转。当咱们从一个竖屏界面push一个横屏界面时,即便横屏界面设置了shouldAutorotate=YES,这个界面也不会变成横屏,可是拿起来设备左右翻转的时分,会发现跟着设备旋转,界面也从横屏变成了竖屏。这便是主动旋转。
手动旋转指的是手动触发旋转,依据经历发现有两个api,UIViewController的+attemptRotationToDeviceOrientation,还有UIDevice的setOrientation:能够调整界面朝向。前者是将界面朝向对齐设备朝向,是规范api;后者是调整设备朝向,是私有api。
假定咱们在许多个竖屏界面中,需求强制横屏某一个界面,假定是子界面能够使用present的办法,假定是push那么就必须要用到这个私有api。

留意事项

其他反正屏适配办法

1、视图适配:经过transform修改layer从而在视图上完成横屏,可是此刻屏幕宽度、状态栏、安全间隔等都保存竖屏状态,这种办法只是适用于横屏弹窗等部分场景;
2、新建Window:由于App的适配是UIWindow为单位,那么理论上是能够新建一个UIWindow来横屏的界面;

反正屏切换告诉

NSNotification告诉

[[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"NSNotification:%@, orientation:%d", note.userInfo, [(UIDevice *)note.object orientation]);
    }];

UIViewController回调

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0));

[北京/广州/深圳] 抖音番茄小说客户端团队,欢迎联络(有加必回)

/post/714156…