在XCode中可以设置App支持的旋转方向。如图所示:
项目中定义了可以支持竖屏(home键在下方),以及横屏的旋转。如果你想让你的App不能旋转,只能竖屏显示,那取消LandScape Left 和LandScape Right 就可以了。
在iOS6以后,原来在UIViewController中用来判断是否支持某个特定方向旋转的接口已经被弃用了
在iOS6后使用
- (BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;
三个函数来完成控制旋转。当shouldAutorotate返回YES的时候,第二个函数才会有效。如果返回NO,则无论你的项目如何设置,你的ViewController都只会使用preferredInterfaceOrientationForPresentation的返回值来初始化自己的方向,如果你没有重新定义这个函数,那么它就返回父视图控制器的
preferredInterfaceOrientationForPresentation的值。
当你的shouldAutorotate返回YES的时候,系统就会用你的ViewController的
supportedInterfaceOrientation的返回值来决定是否要旋转该视图。它的返回值如下:
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),
};
值的含义都非常明确。所以,在你的ViewController里面实现这个函数,就可以有选择的旋转你的视图。
那么读者就已经很清楚如果我们不想让视图旋转,我们只需要在
supportedInterfaceOrientation
中返回表示竖屏的那个值就可以了。
例如笔者的一个VoIP应用,笔者想让除了视频通话的界面能以外的界面都只能竖屏显示。那么笔者在某个ViewController中是这样实现这个三个函数的,选择其中一个ViewController来举例:
刚刚说到,如果不想让这个试图旋转,只要直接让shouldAutorotate返回NO就可以了。这个是可以的。但例外就如笔者的这个ViewController一样,前面说到笔者想让视频通话的界面能旋转,而这个ViewController就是那个视频通话界面的父ViewController。可以想象这样一个情况,当你的电话到来是,你的视频界面出来了,你把手机横放,以便看到的图像能更大更清晰。这时候通话结束了,你的视频界面自动退出,但返回的时候,你的界面是横屏状态的。那么返回父视图控制器的时候,它又是怎么样的状态的呢?
答案是你的父视图也跟着旋转了。是的,虽然你的shouldAutorotate返回了NO,但是你的orientation确实被你的子视图控制器改变了。而且,这个改变是无法被父视图自己修复的,因为你返回了NO。所以处理这个种情况,你需要让你的视图能够旋转,即返回YES,然后在你的
supportedInterfaceOrientations中返回MaskPortrait,表明你的视图只支持竖屏显示。
当你限定了你支持的旋转方向的时候,你必须重新定义preferredInterfaceOrientationForPresentation函数,因为它的返回值必须是你支持的旋转方向中的一种,你返回了MaskPortrait,preferredInterfaceOrientationForPresentation也应该只能返回Portrait这个值。否则就会抛出一个异常。这就是为什么你必须也同时定义它的原因。
好的,这样你的这个视图控制器就会在视频界面横屏退出之后,自动恢复成竖屏显示了。那么目前为止,我们似乎已经解决了问题了。但是当笔者旋转设备的时候……不起效,我的视图随意被旋转。但是明明已经定义了只支持竖屏显示了,为什么还是可以随意旋转呢?
随后在
supportedInterfaceOrientations函数中打印了一个log,发现这个函数只在视图被显示的时候调用了一次,当你旋转设备的时候,这个函数根本没有被调用,也就是说系统在设备旋转的时候没有查询你当前视图所支持的旋转方向。但是苹果的文档应该不会坑爹。所以笔者猜测在旋转的时候,调用的是某个比较底层的视图控制器的
supportedInterfaceOrientations函数。
那么读者们就马上敏感地想到了根视图控制器。一般就是一个UINavigationController或者UITabBarController这样的控制器。笔者的项目使用的是Stroyboard。这个项目最开始的结构如下图所示,这应该是StroyBoard项目常用的结构吧?
最开始的那个OrientationController是一个UINavigationController的子类。同时也项目里面所谓的根视图控制器。这个子类的主要目的就是为了能让其他视图控制器中的上述三个函数能被调用。笔者是这么做的,在这个子类中,重写了这三个函数,通过返回当前在顶层的视图(是否等价于当前可见的视图?)对应函数的值来决定是否可以旋转,支持的旋转方向是什么。如下:
这样,当设备旋转的时候,系统会调用你的根视图控制器的这些函数,而根视图控制器又通过topViewController来获得最前端的视图控制器的旋转属性。那么旋转的结果就总是与你能看到的那个视图控制器所定义的一致了。