这样的题目应该描述得很清楚了吧?对于一些App,我们可能允许它跟随设备旋转,但是对于特定的某个界面,就相反了。例如我们有个界面可以用来显示照片或者观看视频,这样的界面,我们希望它能跟随系统旋转进而有机会横屏观看。但是其它的界面,我们希望它们永远只是竖屏显示的。所以,就产生了如题目所说的需求。
先说目前笔者的开发环境吧,使用XCode5.0和iOS7做开发,不过以下讲的方法在iOS6就可以用。如果版本低于6.0的读者,就需要自己找方法了。
读者应该都知道在项目设置中可以设置App支持的旋转方向。如图所示:
项目中定义了可以支持竖屏(home键在下方),以及横屏的旋转。如果你想让你的App不能旋转,只能竖屏显示,那取消LandScape Left 和LandScape Right 就可以了。
在iOS6以后,原来在UIViewController中用来判断是否支持某个特定方向旋转的接口已经被弃用了
在iOS6后使用
- (BOOL)shouldAutorotate;
- (NSUInteger)supportedInterfaceOrientations;
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;
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项目常用的结构吧?
这样,当设备旋转的时候,系统会调用你的根视图控制器的这些函数,而根视图控制器又通过topViewController来获得最前端的视图控制器的旋转属性。那么旋转的结果就总是与你能看到的那个视图控制器所定义的一致了。但是这里用topViewController是否妥当,如果有好的建议的读者不妨分享一下经验。
最后,什么视图,视图控制器之类的名词突然冒了一堆堆,用词并不十分严谨, 相信各位读者应该能整理出我想表达的那个意思。 而且会有不少错字,因为祖国母亲的生日假期即将来临,思维有些许跳跃,有20%的脑袋已经在异步处理其他事情了~~ 请各位读者见谅。啊……笔者就是个懒人啊……!那么这个简单的问题就到这里吧。
Enjoy your coding.