前言
很长时间没有更新,原因不多说(因为懒),最近比较清闲,想起来写一篇文章来说一下iOS横竖屏轻松切换的过程。 有些需求整体的App只支持竖屏,但是只需要某一部分页面支持横屏,许多的视频类的App都要在竖屏的情况下进行横屏播放,当然还有很多的奇葩需求要横屏来进行实现。
废话少说,进主题
创建项目,App的方向只需要勾选Portrait就行(其实可以不用勾,但是能有几个项目中不勾的呢,我们还是勾上吧)。
前面说可以不勾选因为我们要在AppDelegate中,重写这个方法, 应用程序启动的时候会调用这个方法来给App所需要的屏幕方向:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if([ScreenDirectionManager manager].islandscape) {
return UIInterfaceOrientationMaskLandscape;
}else {
return UIInterfaceOrientationMaskAll;
}
}
其中UIInterfaceOrientationMask是一个NS_OPTIONS, 我们可以随意组合,虽然其中有一些组合好的,但是万一不满足产品的奇葩需求呢,对不对,要提前留一手。
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;
意思内容不用多解释,应该都能知道什么意思。
其中ScreenDirectionManager这个类控制屏幕是否横屏,是一个单例就一个属性是否需要横屏,比较简单。
@interface ScreenDirectionManager : NSObject
+ (instancetype)manager;
@property (nonatomic, assign, getter=islandscape) BOOL landscape;
@end
如果你的App不包括导航栏(UINaviagtionController)或者(UITabbarController),你只需要重写UIViewController里面的三个方法就行,这三个方法是:
// 返回bool值,决定Controller是否自动旋转
-(BOOL)shouldAutorotate
//返回一个Controller支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
//返回现在正在显示的用户界面方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
但是一般的应用至少有个导航栏吧,所以呢你还需要自定义导航栏(UINavigationController),然后在导航栏里面重写这三个方法,如果不重写,就无法达到你想要的效果,感觉就像这个屏幕方向具有传递的性质。但是我感觉UITabbarController的情况应该少数,我这里不讨论这个情况,下次有空再补上,但是我感觉原理应该是一样的,有兴趣的可以自己试一下。
导航栏的屏幕方向和旋转性质要和导航栏最上层的Controller保持一致,所以自定义导航栏重写的三个方法是这样的:
-(BOOL)shouldAutorotate {
return [[self.viewControllers lastObject] shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}
在ViewController中,重写上述的三个方法,返回自己想要的方向,那个改Controller只能是返回的方向,例如:
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate {
return YES;
}
在第二个页面中,也就是跳转的页面中,重写上述的三个方法,返回自己想要的方向,然后在viewWillAppear方法中设定将单例的属性方向改变,然后根据UIDevice的orientation这个属性,根据KVC,强制设置方向,如下;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[ScreenDirectionManager manager].landscape = YES;
if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
//写这句话的目的是保证后面的一句话产生作用,具体不明白为什么,但是不加上会出现bug。有明白的可以告知一下。
[[UIDevice currentDevice] setValue:@(UIDeviceOrientationUnknown) forKey:@"orientation"];
[[UIDevice currentDevice] setValue:@(UIDeviceOrientationLandscapeLeft) forKey:@"orientation"];
}
}
还需要在viewWillDisappear中,将单例的横竖屏属性改变回来,因为supportedInterfaceOrientations这个方法会触发AppDelegate中的方向支持方法。
结论
文章描述的不太清楚的,欢迎留言讨论。Demo