@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
如果app横屏适配没做好,是需要在少数页面支持横屏,那么可以用如下方法实现。
这里为什么只需要Portrait?因为如果开启了Landscape,则在打开屏幕旋转锁的条件下,以横屏模式加载app,会导致app以横屏模式加载,这并不是几乎没做旋转适配的我们所希望的。
- 在AppDelegate.m 中实现如下代理:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
UIInterfaceOrientationMask orientations = UIInterfaceOrientationMaskAllButUpsideDown;
return orientations;
}
因为屏幕还是需要旋转,所以必须要这个代理里面的设置覆盖(1)中Device Orintation的全局设置.
3.实现如下两个扩展:
#import <UIKit/UIKit.h>
@interface UITabBarController (AutoRotate)
@end
#import "UITabBarController+AutoRotate.h"
@implementation UITabBarController (AutoRotate)
- (BOOL)shouldAutorotate
{
return [self.selectedViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [self.selectedViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
@end
#import <UIKit/UIKit.h>
@interface UINavigationController (AutoRotate)
@end
#import "UINavigationController+AutoRotate.h"
@implementation UINavigationController (AutoRotate)
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
// 在支持多个方向的时候,首次进入优先加载的方向,在iOS10下面没有调用
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
@end
这两个扩展的作用:
在以UITabBarController或者UINavigationController作为根视图的时候,将旋转需要调用的三个接口shouldAutorotate,supportedInterfaceOrientations和preferredInterfaceOrientationForPresentation交给具体的UIViewController去实现.
- 在各个想要控制横竖屏的具体页面(UIViewController)上实现以下三个方法:
// 最好都是YES,即使只支持竖屏,因为可能首次以横屏显示的时候不能在转动设备的情况下回到竖屏模式
- (BOOL)shouldAutorotate
{
return YES;
}
// 支持的多个方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
return XXX;
}
// 在支持多个方向的时候,首次进入优先加载的方向,在iOS10下面没有调用
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return XXX;
}
为什么在UINavigationController中要用self.topViewController而不用self.visibleViewController? 因为self.visibleViewController返回的是最上层(最接近观察者)的页面,包括present出来的模态页面,而self.topViewController则是最上层的被push出来的页面.
实验条件: 以UITabBarController作为根视图, 根视图下面挂两个UINavigationController, 两个UINavigationController的rootViewController分别是FirstViewController和SecondViewController,FirstViewController push 出一个PushedViewController, SecondViewController present 一个PresentedViewController. 如下图:
下面是我的实验结论:
( I )在UITabBarController的主界面,我想做到FirstViewController竖屏,SecondViewController横屏,然后再对应页面的三个方法中实现控制. 其结果是, selectedViewController的方向会是接下来被selected的UIViewcontroller的方向,旋转以后才会固定在期望的方向上. 比如一开始FirstViewController是竖屏,点击tab bar 切换到SecondViewController, SecondViewController也会是竖屏; 然后旋转一下屏幕, SecondViewController变为横屏, 且在不切换tab bar的情况下,无论怎么旋转屏幕都是横屏; 然后再切到FirstViewController, FirstViewController变为横屏; 再旋转屏幕, FirstViewController变为竖屏, 且在不切换tab bar的情况下,无论怎么旋转屏幕都是竖屏......
( II )然后将FirstViewController和SecondViewController都固定为竖屏, 且在PushedViewController和PresentedViewController页面,我希望它们都是横屏, 然后再对应页面的三个方法中实现控制. 由FirstViewController push出来的PushedViewController,其初始方向总是与FirstViewController一致, 所以刚开始加载的时候它是竖屏; 旋转屏幕, PushedViewController变成横屏且无法回到竖屏, 再pop 出PushedViewController, FirstViewController仍然是竖屏. 由SecondViewController present出来的PresentedViewController, 正是我们需要的效果; dismiss PresentedViewController之后, SecondViewController的方向也与我们需要的一致.
在测试( II )中, 把UINavigationController扩展中的self.topViewController换成self.topViewController, 其它不变, 则PushedViewController的初始方向也总是和FirstViewController一致, 所以刚开始加载的时候它是竖屏; 再pop 出PushedViewController, FirstViewController的方向由设备的方向决定, 经过旋转后回到竖屏,且无法再旋转成横屏. PresentedViewController的方向是我们需要的方向, 但是在dismiss PresentedViewController之后, SecondViewController的初始方向和PresentedViewController一致, 经过旋转以后回到竖屏,且无法再旋转成横屏.
所以, 在UINavigationController中使用self.topViewController与我们的期望最接近. 而且我还发现:
(1)不管在UINavigationController中使用self.topViewController还是使用self.visibleViewController, push PushedViewController时都不会调用PushedViewController中对应的三个方法,也就是说,这个时候PushedViewController的初始方向肯定和FirstViewController一致. 只有将设备旋转以后才会调用那三个方法(preferredInterfaceOrientationForPresentation在iOS 10中一直不调用,在其它版本中是否调用我还不清楚).
(2)在使用self.visibleViewController的时候:pop PushedViewController或者dismiss PresentedViewController时, self.visibleViewController会指向一个神奇的类UISnapshotModalViewController, 不知道这个是类是什么.有兴趣大家可以探究一下.
所以只剩下最后一个问题, 在push PushedViewController要怎么才能使设备强制旋转到对应的方向呢?
不推荐使用performSelector强行调用UIDevice实例的setOrientation方法, 因为强调这个私有方法可能会导致审核被拒. 推荐使用UIDevice实例的 setValue:forKey:方法来强制旋转屏幕(Key传@"orientation”), 这个方法貌似没有调用私有API,因此不会被拒.但是比较有风险,因为随着iOS的升级, 这个字段很有可能会被改变,导致app crash. 可以用如下方法避免崩溃:
NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];
@try {
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];
} @catch (NSException *exception) {
} @finally {
}
还有一种方法就是使用黑方法:
在PushedViewController的viewDidLoad中加入这段代码, 即先present一个UIViewController, 然后马上dismiss:
UIViewController *vc1 = [[UIViewController alloc] init];
[self presentViewController:vc1 animated:NO completion:nil];
[vc1 dismissViewControllerAnimated:NO completion:nil];
但是没有旋转的动画效果,比较生硬.
还有最后一种方法, 就是用通过旋转View的transform属性来伪造屏幕旋转的假象,但我没尝试过,感觉会有各种诡异的问题.