iOS学习笔记03--屏幕旋转

前提:
你需要把controller.view作为window的subview,也即是需要设置window的rootViewController,直接把controller的view添加到window上是不行的,如果我们不设置window的rootViewController,那么屏幕局的旋转只能有UIApplication对象来控制,而且屏幕旋转时controllers也将拿不到通知,导致转屏失效。

  • 1
    从iOS6开始,在controller中我们可以通过覆盖
-(UIInterfaceOrientationMask)supportedInterfaceOrientations;

这个方法来限制屏幕旋转的方向,如果不重写,那么在iPad上返回UIInterfaceOrientationMaskAll(所有方向),iphone则返回UIInterfaceOrientationMaskAllButUpsideDown(除了上下颠倒外的方向),如果某个controller不需要在多个方向展示其内容,那么就不需要重写此方法。官方解释为:

There is little need to override this method unless the content managed by the view controller must only be displayed in a subset of these orientations. If you override this method, your implementation must return a bitwise combination of UIInterfaceOrientationMask values.

在iOS5及以下则采用:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation


  • 2
    从ios6开始,UIKit开始使用UIApplication和最顶层的controller(topmost controller)来决定支持的方向,在Info.plist中有一个字段UISupportedInterfaceOrientations,它包含了你的应用所支持的屏幕方向,它和你在target的general中的设置是一一对应的,通常我们修改其中的一个地方,另外一个地方就会跟着发生变化:
plist中的设置.png

target中的设置.png

对于 UIApplication,可以在APPDelegate中通过以下方法来设置应用所支持的方向:

//此方法替换了之前在plist和target中设置的屏幕方向,例如:如果之前设置的是只支持竖屏,在这可以修改为只支持横屏或其他,那么修改完成后应用就支持了现在所设置的方向,当然我们仍然可以设置支持多方向,和在plist及target中设置的效果是等同的
-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
    return UIInterfaceOrientationMaskPortrait;
}
  • 特别注意:
    这里的最顶层的controller(topmost controller)定义一般情况是window的rootViewController,只有在present到另外一个controller后tiomost controller 才会变成被present的那个controller,也就是topmost controller只有两种情况,要么是rootViewController,要么就是present的后的controller,特别要与navigation controller的topViewController和visibleController区分开,如果解释的不是很明白,那么我们来看一下官方解释:

Important: Here, the topmost view controller refers to the window's root view controller unless another view controller is currently presented, in which case, the presented view controller becomes the topmost view controller for the duration it is presented. This should not be confused with thetopViewController
of a navigation controller.

这也就是为什么当我们设置NAVigationControllerrootViewController,在push到的子页面再去改变子controller的屏幕方向时没有效果的原因,因为push到的子controller不是topmost controller,自然也就无法直接去控制屏幕的旋转,网上的解决方法很多说是在navigation controller中去调用它的topViewController里面的方法,我试了一下发现并没有什么用,如下:

//在navigation controller中重写
-(BOOL)shouldAutorotate{
    return [self.topViewController shouldAutorotate];
}
-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return [self.topViewController supportedInterfaceOrientations];
}

不知道是我哪个地方漏掉一部分东西还是其它什么原因,push过去后界面仍然是不会发生旋转的。

  • 3 iOS6+ 的强制横屏
    iOS5就不说了,从iOS6及以后控制屏幕的方向的方法,分两种情况:
    (1) vc是present进来的,可以直接重写下面的方法:
//是否支持自动旋转,如果要强制横屏则需要设为NO(跳转到此页面需要用present的方式,否则强制横屏没有作用)
-(BOOL)shouldAutorotate{
    return NO;
}
//首选方向(如果支持的方向比较多,可以设置此方向,那么刚进入这个界面时显示的就是此方向)
-(UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return UIInterfaceOrientationLandscapeRight;
}
//在某个controller中可以重写此方法来达到设置支持的屏幕方向的目的,在iPad上默认返回UIInterfaceOrientationMaskAll,而在iphone上默认返回UIInterfaceOrientationMaskAllButUpsideDown,如果controller上的内容仅仅需要在某个方向上去展示那么需要覆盖此方法,否则不需要
-(UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

(2) vc是push进来的,上面的方法就不会起作用了,我现在所能想到的方法是旋转view:

//进入界面旋转window
-(void)viewWillAppear:(BOOL)animated{
    self.navigationController.view.window.transform = CGAffineTransformMakeRotation(M_PI_2);
    self.navigationController.view.window.bounds = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.height, [[UIScreen mainScreen] bounds].size.width);
}
//退出界面,恢复window
-(void)viewWillDisappear:(BOOL)animated{
    self.navigationController.view.window.transform = CGAffineTransformMakeRotation(0);
    self.navigationController.view.window.bounds = CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
}

当屏幕旋转时系统会为我们发出一个通知:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(screenChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];

我们可以监测此通知做一些事情,比如界面的重新布局等等,但是如果我们采用push进来的方法而且是通过改变window来达到旋转(实际上是假旋转)的话,那么系统不会发出通知,我们自然也就监听不到。而在实际应用中,如果想要某个界面强制横屏,那么用present的方式就已经完全可以满足要求了,而且很多应用采用的也是present的方式,当然谁如果有解决push更好的解决办法,还请赐教,谢谢!


  • 总结
    1、全局控制app是否支持屏幕旋转有三种方式可选:target、plist、和UIApplication
    2、present进来的vc可以直接重写三方法来控制屏幕旋转
    3、push进来的vc通过改变window的transform来实现伪旋转

  • End

你可能感兴趣的:(iOS学习笔记03--屏幕旋转)