在日常开发过程中,经常会遇到转屏的需求,最近遇到一个转屏相关的bug,顺带着总结下iOS端实现转屏需要做的事情。
什么是设备方向,什么是视图方向
首先需要明确两个概念,设备方向(UIDeviceOrientation)和视图方向(UIInterfaceOrientation)
UIDeviceOrientation:
不受锁定屏幕方向的影响,通过
[UIDevice currentDevice].orientation
客观反映出当前设备所处的方向,与视野中正在展示视图的方向无关,该属性只读,无法修改
该属性的变化可以通过监听UIDeviceOrientationDidChangeNotification来实时获得设备方向的变化
UIInterfaceOrientation:
多数的旋转都需要通过旋转controller来实现。controller的方向也就是我上面提到的视图方向,使用该枚举UIInterfaceOrientation来表达。
如果想获得当前的视图方向,可以通过以下代码获得
[UIApplication sharedApplication].statusBarOrientation
视图方向的改变一定是由于设备方向发生了变化,设备方向的改变也只能是由于物理改变造成的。但是有一个例外,会在后面强制转屏部分详细说明。
如何让Controller随设备方向旋转
1. 配置App支持的视图方向
首先需要配置下当前App能支持的视图方向,所有使用的controller所能支持的视图方向是该配置的子集;
可以通过以下两种方法进行配置:
方法1,在Xcode选项中配置:
在Xcode->工程->General->Deployment Info中对Device Orientation进行配置,注意这里的Device Orientation不是上面所说的设备方向
方法2,在AppDelegate中通过代码配置:
在AppDelegate中实现如下方法:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskAll;
}
如果同时使用了两种方式,那第二种使用代码的方式会覆盖第一种在Xcode中配置的方式
2. 配置Controller支持的方向
代码很简单,表示当前Controller是否支持旋转,支持的方向都有什么,如下:
//是否支持转屏
- (BOOL)shouldAutorotate
{
return YES;
}
//在支持转屏的前提下,返回具体支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;
}
但是这段代码具体要写在哪个viewController中才有效果呢?
一个viewController是否可以旋转并不是由他自己决定的,而是由rootViewController或者最上层被present上来的viewController来决定的。
每个App都有一个rootViewController,如果没有通过present的方式推入controller,那么rootViewController支持的方向就决定了视图方向;
如果有通过present的方式推入controller,那么最近的一次present操作对应的controller就决定了视图方向;
以上的代码需要放在上面说到的决定了视图方向的controller中
还有一个回调方法,决定了viewController出现时的视图方向,该回调方法只对present方式进入界面的viewController有效果
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
当然还有一点最重要的,记得要在下拉框中打开设备的转屏锁,要不然怎么转ViewController都不会跟着转的
关于强制转屏
在上文中提过,设备方向改变一定是由于物理改变造成的,其实这并不绝对
苹果的API曾经是允许修改[UIDevice currentDevice].orientation的,后来这个接口被干掉作为私有API了
但是我们还是可以通过一些其他方式绕过苹果的限制来设置[UIDevice currentDevice].orientation,设备方向一旦改变,再符合上述视图方向改变的条件,视图方向就会被旋转,从而达到强制转屏的效果
在实际开发过程中也会遇到类似的场景,比如播放器的全屏操作,就是一次强制的视图方向改变
代码如下:
NSNumber *orientationTarget = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
[[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];
关于UINavigationController的旋转
其实道理是一样的,只不过如果present上来一个navigationController的时候,你需要想办法让这个navigationController去实现上述的shouldAutoRotate和supportedInterfaceOrientations方法,可以通过类别或继承重写的方式,这里也就不详述了