天天看点

iOS屏幕旋转那些事

    前些日子处理了一些关于页面中的屏幕旋转的事情,需要根据某个页面的不同旋转方向去做不同的处理,现在再次总结一下,以在心里有个系统的体系,也可以当成工具文章查询。

    首先来看第一个方法:

[UIDevice currentDevice].orientation
           
iOS屏幕旋转那些事

    可以看到这个属性返回的是设备的物理方向,并且我们发现这个属性是只读的, UIDeviceOrientation 是硬件设备的方向,这个方向是随着硬件自身改变而改变的,只能取值,不能赋值。

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,             //值是0,以下依次递增1
    UIDeviceOrientationPortrait,            // Device oriented vertically, home button on the bottom 
    UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top
    UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right
    UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left
    UIDeviceOrientationFaceUp,              // Device oriented flat, face up  //设备屏幕朝上
    UIDeviceOrientationFaceDown             // Device oriented flat, face down //设备屏幕朝下
} __TVOS_PROHIBITED;
           

    上面是UIDeviceOrientation的枚举,我们的设备处于三维空间中,因此有6个方向。

    我们可以通过添加:

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(deviceChange) 
name:UIDeviceOrientationDidChangeNotification object:nil];
           

    这个通知来监听当前设备的物理方向:

-(void)deviceChange{
    NSLog(@"设备旋转");
    NSLog(@"当前设备方向:%ld",[UIDevice currentDevice].orientation);
}
           

    接着来看第二个方法:

[UIApplication sharedApplication].statusBarOrientation
           
iOS屏幕旋转那些事

    从上图中我们可以看到,这个值返回的是当前app的status bar的方向,即状态栏的方向,并且该属性也是只读的。

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,  
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} __TVOS_PROHIBITED;
           

    这是UIInterfaceOrientation的枚举,因为状态栏所处于一个平面,所以只有四个方向。

    注意看最后两项:    

    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
           

    为什么正好是相反的呢?

    这样想,当设备向左旋转的时候,设备中的画面需要向右旋转,因此是相反的(不太清楚的打开模拟器旋转几下屏幕,注意看状态栏的方向改变和设备的方向改变,一对比就可以明白了)。

    同的样我们可以监听:

UIApplicationDidChangeStatusBarOrientationNotification
           

    这个事件来监听当前的设备的状态栏的方向,

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(change) 
name:UIApplicationDidChangeStatusBarOrientationNotification object: nil];
           
-(void)change{
    NSLog(@"状态栏改变了");
    UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
    NSLog(@"%ld",(long)orientation);
}
           

    注意的点:

iOS屏幕旋转那些事

    1.设置状态栏方向的方法已经被弃用了。

    2.我们可以使用下面这个枚举,使用起来更加便捷。

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),
} __TVOS_PROHIBITED;
           

    如何控制:

    (1)被动:

    被动的意思就是,我们通过改变支持的自动旋转的方向来让app在有些方向上可以跟着旋转而有些方向上不行

 * [General]->[Deployment Info]中:

iOS屏幕旋转那些事

 * 或是在info.plist中:

iOS屏幕旋转那些事

    虽然看起来一个是Device Orientation一个是interface orientations但是这两个地方是同步的,表示界面支持的自动旋转的方向,

而且这里对Device Orientation的改变不会影响app内获取设备的物理方向。

* 在AppDelegate中:

-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
}
           

    这里也可以设置,可以结合前面那个 UIInterfaceOrientationMask

* 在具体的UIViewController中:

//是否会跟随设备方向自动旋转,如果设置为NO,后两个方法不再调用
- (BOOL)shouldAutorotate {
    return YES;
}
//返回直接支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskPortrait;
}
//返回最优先显示的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationPortrait;
}    
           
1.这三种方式控制规则的交集就是一个viewController的最终支持的方向;
如果最终的交集为空,在iOS6以后会抛出UIApplicationInvalidInterfaceOrientationException崩溃异常。
2.第二个方法,在iPad上的默认返回值是UIInterfaceOrientationMaskAll,iPhone上的默认返回值是UIInterfaceOrientationMaskAllButUpsideDown;
3.在前面DeviceOrientation即使全部勾选了,若要iPhone支持UpsideDown,也要在viewcontroller里重写第二个方法。返回包含UpsideDown的方向;
4.第三个方法,比如同时支持Portrait和Landscape方向,但想优先显示Landscape方向,那软件启动的时候就会先显示Landscape,在手机切换旋转方向的时候仍然可以在Portrait和Landscape之间切换;
5.如果关闭了系统的横竖屏切换开关,即系统层级只允许竖屏时,再通过上述方式获取到的设备方向将是UIDeviceOrientationPortrait。UIInterfaceOrientation也将不会改变。
6***.第三种方式只有在当前viewController是window的rootViewController。或者是通过presentModalViewController而显示出来的.才会生效。作用于viewController及其childViewController。否则UIKit并不会执行上述方法。
           
****作用于其childViewController单独设置某个viewController并没有效果。这种情况主要可以通过下面几种方法解决:
-(UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return self.selectedViewController.supportedInterfaceOrientations;
}
-(BOOL)shouldAutorotate
{
    return [self.selectedViewController shouldAutorotate];
}
 
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
    return [self.selectedViewController preferredInterfaceOrientationForPresentation];
}
           
UINavigationController 使用self.topViewController
UITabBarController 使用self.selectedViewController
然后在viewController重写这三个方法,这样就巧妙的绕开了UIKit只调用rootViewController的方法的规则. 把决定权交给了当前正在显示的viewController.
           

    (2)主动(有风险):

NSNumber *orientation = [NSNumber numberWithInt:targetOrientation]; [[UIDevice currentDevice] setValue:orientation forKey:@"orientation”];

通过KVC的方式给orientation赋值,来强制设置当前页面的方向,据说可以通过苹果审核,不太清楚。 上述setValue:forKey:方法有一个问题,如果将要设置的目标方向与当前物理设备的方向一样的话,上述方法会不起作用。

请看这个

既然直接通过setValue:forKey:设置目标方向不起作用,那么猜想可以先设置一个unknown方向欺骗系统,然后再设置目标方向,这时目标方向跟unknown方向是不同,系统就会生效这种设置。 NSNumber *orientationUnknown = [NSNumber numberWithInt:0];  [[UIDevice currentDevice] setValue:orientationUnknown forKey:@"orientation"]; 

NSNumber *orientationTarget = [NSNumber numberWithInt:orientation];  [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"]; 

参考 :http://www.cocoachina.com/ios/20180323/22747.html