Morris_ 2018.11.24
前言
最近做一个关于在线视频互动的iPad项目,部分界面只要横屏,部分界面可以横竖屏转换。看了看别家做的项目,有些是只做竖/横屏,有些是支持了横竖屏转换,在iPad上做好横竖屏支持给用户的体验还是挺不错的,有很多应用对横竖屏的转换做的很好,用起来很舒服,很合理,和用iPhone的体验还是挺不一样的。
一、基础知识
1、UIDeviceOrientation & UIInterfaceOrientation
The physical orientation of the device.
设备的方向,即手机或者iPad的方向,可以理解为设备是直立、倒立还是平躺,扣着放等。
//设备方向未知
UIDeviceOrientationUnknown,
//设备直立,顶部朝上,即home键在下,
UIDeviceOrientationPortrait,
//设备倒立,顶部朝下,即home键在上
UIDeviceOrientationPortraitUpsideDown,
//设备横立,顶部在左,home键在左
UIDeviceOrientationLandscapeLeft,
//设备横立,顶部在又,即home键在又
UIDeviceOrientationLandscapeRight,
//设备面朝上平方在桌面上
UIDeviceOrientationFaceUp,
//设备面朝下,扣在桌面上
UIDeviceOrientationFaceDown
The orientation of the app's user interface.
App的界面方向,即应用里的某个界面展示方向。
//界面方向未知
UIInterfaceOrientationUnknown,
//界面是竖直展示的,一般手机App界面的方向都是竖直的
UIInterfaceOrientationPortrait,
//界面是竖直展示的,只是上下颠倒了一下
UIInterfaceOrientationPortraitUpsideDown,
//界面是横着显示的,home键在左
UIDeviceOrientationLandscapeRight,
//界面是横着显示的,home键在又
UIDeviceOrientationLandscapeLeft
UIDevice的orientation是设备顶部的方向,所以UIDeviceOrientationLandscapeLeft指的是设备顶部在左侧,即home键在又。
两者的区别和联系:UIDeviceOrientation是设备的方向,UIInterfaceOrientation是应用程序操作界面的方向,设备方向是不与App的界面操作方向有必然的绑定关系的。设备的方向变化可以导致应用界面方向变化,这要看在应用中是如何设置了。
2、UIDeviceOrientationDidChangeNotification & UIApplicationDidChangeStatusBarOrientationNotification
第一个是设备方向改变的监听,第二个是状态栏方向改变的监听。设备方向改变可以造成状态栏方向变化,但状态栏方向变了,设备方向不一定改变。
//屏幕旋转监听
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil];
二、设置应用支持的屏转方向
1、在TARGET->Deployment Info->Device Orientation下勾选需要支持的屏转方向设置
这里需要注意:
Landscape的设置和device的orientation的左右是反的,和interfaceOrientation是一致的。
此处的设置如果是iPhone的话设置Upside Down是无效的,手机不支持Upside Down,即倒立方向的设置?
2、在AppDelegate的方法中设置
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED {
return UIInterfaceOrientationMaskAllButUpsideDown;
}
在AppDelegate的方法中设置后,会将1中的设置覆盖掉。
三、设置某个界面支持的屏转方向
1、如果该界面是被present出来的,则直接在该界面中试下如下方法即可生效
//当前页面是否支持屏幕旋转
- (BOOL)shouldAutorotate {
return YES;
}
//当前页面支持的屏转方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
//从上已界面进入时的初始方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft;
}
这里设置了初始方向是home键在又,支持的屏转是横屏。
如果未设置初始的屏幕方向的话,会和支持的上一个界面方向保持一致。
注意:如果该页面是被push出来的,这种设置会失效。
2、如果该页面是被push进来的,需要在navigationController和前一个后一个界面中都做处理,否则你会发现在pop回来后会发现第一个界面支持的屏转方向和第二个界面不一致的就不能旋转了。
navigationController中:
- (BOOL)shouldAutorotate {
return self.visibleViewController.shouldAutorotate;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return self.visibleViewController.supportedInterfaceOrientations;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return self.visibleViewController.preferredInterfaceOrientationForPresentation;
}
前一个controller中:
这个界面只支持竖屏
//当前页面是否支持屏幕旋转
- (BOOL)shouldAutorotate {
return YES;
}
//当前页面支持的屏转方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
//从上已界面进入时的初始方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
后一个controller中:
这个界面只支持横屏
//当前页面是否支持屏幕旋转
- (BOOL)shouldAutorotate {
return YES;
}
//当前页面支持的屏转方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
//从上已界面进入时的初始方向(push进来的界面这种设置无效)
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft;
}
从第一个界面中push到第二个界面,发现第二个界面初始的屏幕方向并不是设置的UIInterfaceOrientationLandscapeLeft,则需要强制旋转屏幕,见强制转屏内容。
四、强制转屏
- (void)rotateScreen:(UIInterfaceOrientation)orientation
{
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)])
{
NSNumber *num = [[NSNumber alloc] initWithInt:orientation];
[[UIDevice currentDevice] performSelector:@selector(setOrientation:) withObject:(id)num];
[UIViewController attemptRotationToDeviceOrientation];
}
SEL selector=NSSelectorFromString(@"setOrientation:");
NSInvocation *invocation =[NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
[invocation setSelector:selector];
[invocation setTarget:[UIDevice currentDevice]];
int val = orientation;
[invocation setArgument:&val atIndex:2];
[invocation invoke];
}
五、可能会碰到的问题
1、iPhone旋转横屏后,状态栏去了哪里?
原来是横屏的时候被系统给隐藏了,竖屏的时候又显示出来了。在基controller中作如下处理:
//设置状态栏转屏时不隐藏
- (BOOL)prefersStatusBarHidden {
return NO;
}
2、- preferredInterfaceOrientationForPresentation:方法执行机制是什么?
当从上一个界面到当前界面时,此方法会被调用,一次来设置当前页面初始的展示方式。
3、在viewController里面写了设置屏转的三个方法,为什么屏转控制是无效的?
从第一个界面push到第二个界面,这种情况下,第二个界面的默认方向就是前一个界面的方向。
六、闲话
一般的,如果后一个界面没有明确的设计展示方向的话,应该和它前一个界面的屏幕方向保持一致。
如果后一个界面需要转屏的话,转屏设置应该在后一个界面中完成。
对于iPad应用来说,如果同时支持横竖屏模式的话,在用户体验上会增强很多,苹果也是很支持iPad同时做好横竖屏的转换的。