底子概念
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关于横竖屏的三个方法:
- shouldAutorotate,页面是否答应自动旋转,被弃用api:-shouldAutorotateToInterfaceOrientation的代替者;默认值为YES,表示当前界面答应跟随设备旋转而自动旋转;
- supportedInterfaceOrientations,该界面支持的界面朝向,可以返回四个朝向的恣意组合,iPad默认值是四个朝向都支持,iPhone默认值是除了UpsideDown的三个朝向。这个方法回调的前提是shouldAutorotate=YES。
- preferredInterfaceOrientationForPresentation,该界面被present出来的界面朝向,可以返回四个朝向的恣意组合。如果没有返回,则present时和原来界面保持一致。
AppDelegate干系
AppDelegate的supportedInterfaceOrientationsForWindow方法,根据须要返回当前window是否支持横屏。
- (UIInterfaceOrientationMask)applicationUIApplication *)application supportedInterfaceOrientationsForWindowUIWindow *)window;工程设置干系
在xcode的工程设置的General可以设置iPhone和iPad的页面朝向支持。
横竖屏切换实例
竖屏界面怎样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)applicationUIApplication *)application supportedInterfaceOrientationsForWindowUIWindow *)window { if ([self.navigationVC.topViewController isKindOfClassandscapeViewController.class]) { return UIInterfaceOrientationMaskLandscapeRight; } else { return UIInterfaceOrientationMaskPortrait; }}// LandscapeViewController内部实现- (void)viewDidLoad { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelectorselector(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)applicationUIApplication *)application supportedInterfaceOrientationsForWindowUIWindow *)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)viewWillTransitionToSizeCGSize)size withTransitionCoordinatorid <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0)); |