总结iOS App开发中控制屏幕旋转的几种方式
在ios6之前的版本中,通常使用 shouldautorotatetointerfaceorientation 来单独控制某个uiviewcontroller的方向,需要哪个viewcontroller支持旋转,只需要重写shouldautorotatetointerfaceorientation方法。
但是ios 6里屏幕旋转改变了很多,之前的 shouldautorotatetointerfaceorientation 被列为 deprecated 方法,查看uiviewcontroller.h文件也可以看到:
// applications should use supportedinterfaceorientations and/or shouldautorotate..
- (bool)shouldautorotatetointerfaceorientation:(uiinterfaceorientation)tointerfaceorientation ns_deprecated_ios(2_0, 6_0);
程序将使用如下2个方法来代替:
- (bool)shouldautorotate;
- (nsuinteger)supportedinterfaceorientations;
除了重写这个2个方法,ios6之后要旋转还有一些需要注意的地方,下面会细述。另外还有一个硬性条件,需要在info.plist文件里面添加程序支持的所有方向,可以通过以下2种方式添加
1.
2.
另外要兼容ios6之前的系统,要保留原来的 shouldautorotatetointerfaceorientation 方法,还有那些 willrotatetointerfaceorientation 等方法。
自动旋转设置:
控制某个viewcontroller旋转并不是像ios5或者ios4一样在这个viewcontroller里面重写上面那2个方法,而是需要在这个viewcontroller的rootviewcontroller(根视图控制器)里面重写,怎么解释呢?就是最前面的那个viewcontroller,直接跟self.window接触的那个controller,比如以下代码:
uiviewcontroller *viewctrl = [[uiviewcontroller alloc] init];
uinavigationcontroller *navctrl = [[uinavigationcontroller alloc] initwithrootviewcontroller:viewctrl];
if ([window respondstoselector:@selector(setrootviewcontroller:)]) {
self.window.rootviewcontroller = navctrl;
} else {
[self.window addsubview:navctrl.view];
}
如果需要设置viewctrl的旋转,那么不能在uiviewcontroller里面重写shouldautorotate和supportedinterfaceorientations方法,而是需要在navctrl里面设置,又因为uinavigationcontroller是系统控件,所以这里需要新建一个uinavigationcontroller的子navigationcontroller的子类,然后在里面实现shouldautorotate和supportedinterfaceorientations方法,比如:
-(nsuinteger)supportedinterfaceorientations{
return uiinterfaceorientationmaskallbutupsidedown;
}
- (bool)shouldautorotate{
return yes;
}
eg1:如果上面的例子是self.window.rootviewcontroller = viewctrl,而不是navctrl,那么上面的那2个控制旋转的方法就应该写在uiviewcontroller里面!
eg2:如果viewctrl又pushviewcontroller到viewctrl2,需要设置viewctrl2的旋转,怎么办呢? 还是在navctrl里面控制,因为viewctrl和viewctrl2的rootviewcontroller都是navctrl,一般的写法都是
uiviewcontroller *viewctrl2 = [[uivewcontroller alloc] init];
[self.navigationcontroller.navigationcontroller pushviewcontroller:viewctrl2 animated:yes];
所以要控制一个uinavigationcontroller push到的所有viewcontroller的旋转,那么就得在navctrl里面区分是哪个viewcontroller,以便对他们一一控制!同样如果rootviewcontroller是uitabbarcontroller,那么需要在子类化的uitabbarcontroller里面重写那2个方法,然后分别控制!
但是有时候我初始化uinavigationcontroller的时候,并不知道所有我所有需要push到的viewcontroller,那么这里有一个通用的方法,就是让viewcontroller自己来控制自己,首先在navctrl里面的实现方法改为以下方式:
- (bool)shouldautorotate
{
return self.topviewcontroller.shouldautorotate;
}
- (nsuinteger)supportedinterfaceorientations
{
return self.topviewcontroller.supportedinterfaceorientations;
}
全部调用self.topviewcontroller,就是返回当前呈现出来的viewcontroller里面的设置,然后在viewctrl、viewctrl2等等这些viewcontroller里面重写shouldautorotate和supportedinterfaceorientations,以方便设置每个viewcontroller的旋转
eg3:如果viewctrl 是 presentmodalviewcontroller 到 viewctrl3,那么viewctrl3的旋转设置就不在navctrl里面了!如果presentmodalviewcontroller的viewcontroller是navcontroller、tabbarcontroller包装过的viewctrl3,那么就应在新包装的navcontroller、tabbarcontroller里面设置,如果是直接presentmodalviewcontroller到viewctrl3,那么就在viewctrl3里面设置
手动旋转
手动旋转也有2种方式,一种是直接设置 uidevice 的 orientation,但是这种方式不推荐,上传appstore有被拒的风险:
if ([[uidevice currentdevice] respondstoselector:@selector(setorientation:)]) {
[[uidevice currentdevice] performselector:@selector(setorientation:) withobject:(id)uiinterfaceorientationportrait];
}
第二种是假旋转,并没有改变 uidevice 的 orientation,而是改变某个view的 transform,利用 cgaffinetransformmakerotation 来达到目的,比如:
self.view.transform = cgaffinetransformmakerotation(m_pi/2)
下面讲解采用第二种方式的各版本手动旋转:
思想是首先设置 statusbarorientation,然后再改变某个view的方向跟 statusbarorientation 一致!
那既然是旋转,最少也得有2个方向,那么还是少不了上面说的那个硬性条件,先在plist里面设置好所有可能需要旋转的方向。既然是手动旋转,那么就要关闭自动旋转:
- (bool)shouldautorotate{
return no;
}
手动触发某个按钮,调用方法,这个方法的实现如下:
[[uiapplication sharedapplication] setstatusbarorientation:uiinterfaceorientationlandscaperight];
self.view.transform = cgaffinetransformmakerotation(m_pi/2);
self.view.bounds = cgrectmake(0, 0, kscreenheight, 320);
注意:
1. 只需要改变self.view.transform,那么self.view的所有subview都会跟着自动变;其次因为方向变了,所以self.view的大小需要重新设置,不要使用self.view.frame,而是用bounds。
2. 如果shouldautorotate 返回yes的话,下面设置setstatusbarorientation 是不管用的!setstatusbarorientation只有在shouldautorotate 返回no的情况下才管用!
强制旋转屏幕
最近接手了一个项目,正常情况下使用查看图片是没问题的。
用到了 mwphotobrowser 这个第三方图片浏览库。
不过发现了一个问题,就是设备横屏modal这mwphotobrowser的时候,发生了图片位置错乱。
实在没办法,所以想到了一个馊主意。
就是modal的时候使用代码把设备强制旋转回去。
//uidevice+wj.h
@interface uidevice (wj)
/**
* 强制旋转设备
* @param 旋转方向
*/
+ (void)setorientation:(uiinterfaceorientation)orientation;
@end
//uidevice+wj.m
#import "uidevice+wj.h"
@implementation uidevice (wj)
//调用私有方法实现
+ (void)setorientation:(uiinterfaceorientation)orientation {
sel selector = nsselectorfromstring(@"setorientation:");
nsinvocation *invocation = [nsinvocation invocationwithmethodsignature:[self instancemethodsignatureforselector:selector]];
[invocation setselector:selector];
[invocation settarget:[self currentdevice]];
int val = orientation;
[invocation setargument:&val atindex:2];
[invocation invoke];
}
@end