iOS横竖屏转换记录

1. 横竖屏方向枚举

关于横竖屏一共有三种枚举,UIInterfaceOrientation,UIInterfaceOrientationMask,UIDeviceOrientation。

1.1 UIInterfaceOrientation与UIDeviceOrientation

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

对于iOS设备来讲,屏幕状态由以上五种状态。上下翻转还是很好区分的,左右旋转可能就不是很好区分。

但是,在处于竖屏和上下翻转的状态下这两个枚举值是一样的,而当处于横屏时,这两个值刚好相反。
所以在有时你发现跟你预期的翻转方向不一样的时候,可能你用错了枚举。

UIDeviceOrientation是设备的当前所处的方向,而且事实上他有6个值

typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,
    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
}

分别对应iPhone位置方向,竖直,上下翻转,向左翻转,向右翻转,屏幕朝上,屏幕朝下。

关于横屏如何去分左右,其实API中的注释已经说明,当处于UIDeviceOrientationLandscapeLeft,home键在右侧,当处于UIDeviceOrientationLandscapeRight,home键在左侧。

所以,UIDevice顾名思义,事实上就是用来判断设备方向的。

UIInterfaceOrientation即当前页面的方向。

在设备进行横屏旋转的时候,为了横屏时上下不翻转,所以当Device处于Left时,界面应该是Right旋转。这样才不会使横屏时内容上下翻转。所以也明白了为什么在处于横屏时,为什么他们俩的值是正好相反的。

所以,对于横竖屏适配,使用的枚举一定要看好,使用UIInterfaceOrientation,不要搞反。

1.2 UIInterfaceOrientationMask

其实苹果还是给了我们更清晰和方便的枚举如下:

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

事实上我们在横竖屏适配时,最常用的是这个枚举。这个枚举详细的列举了各种你需要的情况。

2. 开启横竖屏权限

开启横竖屏的方式有两种:

  • 在项目中直接进行勾选:


    image.png

可以看到这种勾选方式允许你进行四个方向的配置,并且这种勾选方式会直接在你的项目plist文件中添加:


image.png

但是由于在这里配置是对项目启动时lanuch界面产生影响,而往往我们又没有对launch进行横竖屏适配,所以在这个时候我们需要使用第二种方式进行配置。

如果不想在启动页显示状态栏,info.plist文件中配置:
Status bar is initially hidden 为YES

  • 在项目中的AppDelegate文件中进行配置
#pragma mark - InterfaceOrientation //应用支持的方向
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return UIInterfaceOrientationMaskAllButUpsideDown;
}

启动页是竖屏,进入之后就是横屏了。

搭配UIInterfaceOrientationMask使用,你可以很方便的让你项目开启你所需要的横竖屏权限和限制条件。

3.在VC中如何控制横竖屏

我们都知道MVC架构,那么显而易见,在我们开启了项目的横竖屏的限制之后,需要在ViewController进行相应的配置,才能真正实现横竖屏。Nav Tab如果需要的话,也需要设置。

//是否可以旋转
- (BOOL)shouldAutorotate {
    return YES;
}
// 支持的方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskPortrait;
}
//由模态推出的视图控制器 优先支持的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
    return UIInterfaceOrientationPortrait;
}
//是否隐藏状态栏
- (BOOL)prefersStatusBarHidden {
    return NO;
}
//状态栏样式
- (UIStatusBarStyle)preferredStatusBarStyle {
    return UIStatusBarStyleDefault;
}

而对于横竖屏,手机端一般有两种情况,一种是手机没有开启横竖屏锁定,用户将手机横屏时触发的。对于第一种情况,我们只需要在VC中添加:

// 开启自动转屏
- (BOOL)shouldAutorotate {
    return YES;
}

另一种是我们在项目中的某些条件下强行让屏幕横屏,例如大图预览,视频播放,等等。而对于这种情况,我们可以使用下面这两种方法,都可以实现效果:

- (void)setInterfaceOrientation:(UIDeviceOrientation)orientation {
    if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
        [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"];
    }
}

这两个方法只有在- (BOOL)shouldAutorotate( return YES; )时,才会生效。并且请注意使用的枚举值的不同。

4.横竖屏控制优先级

在我们接手一个项目后,说要添加一个某个界面横竖屏需求时,发现按照上面的方式配置了一圈,发现还是转!不!成!功!What F***!!!

事实上在这里我们要了解一个问题,就是关于横竖屏控制的优先级。对于限于VC范围来讲优先级最高的是当前的window的rootViewController,而往往我们的项目结构是容器视图控制器控制VC,tabBarController控制navigationController之后是VC,而横竖屏控制的优先级也是跟你的项目架构一样。而且是一旦优先级高的关闭了横竖屏配置,优先级低的无论如何配置都无法做到横竖屏。所以在你接受这个需求的时候,你需要看一下根视图的配置。

对于这种情况,我们有两种处理方式,一种是通过模态的方式跳转的下个VC,这个VC是隔离出来的,不在你之前的容器里,不会受到rootViewController的影响。

而另一种我们需要改造一下根视图的配置:

-(BOOL)shouldAutorotate {  
    return [[self.viewControllers lastObject] shouldAutorotate];  
}  

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {  
     return [[self.viewControllers lastObject] supportedInterfaceOrientations];  
}  

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {  
    return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];  
}

或者:

-(BOOL)shouldAutorotate {
    if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) {
        return YES;
    }
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) {
        return UIInterfaceOrientationMaskLandscapeLeft;
    }
    return UIInterfaceOrientationMaskPortrait;
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    if ([[self.viewControllers lastObject]isKindOfClass:[NewViewController class]]) {
        return UIInterfaceOrientationLandscapeLeft;
    }
    return UIInterfaceOrientationPortrait;
}

可以看到我们通过获取push栈中的最后一个VC的属性,或指定特殊的VC来进行rootViewController的横竖屏设置。

当然也可以通过NSNotificationCenter或者NSUserDefaults的方式对这里的值进行设置,在这里我就不过多赘述了。

总之要知道优先级的问题,general == appDelegate >> rootViewController >> nomalViewController

明白了权限的优先级以及开启的方法我想转屏就很显而易见了。

5.屏幕旋转监听以及相关处理

做好配置后我们就可以对各个方向进行配置,因此需要监听屏幕的各个方向,我们可以用UIApplicationDidChangeStatusBarOrientationNotification来监听,代码如下

//监听UIApplicationDidChangeStatusBarOrientationNotification
    //手机锁定竖屏此通知方法也失效了
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(statusBarOrientationChange:)
                                name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];

然后通过通知回调获取对应的方向

//界面方向改变的处理
- (void)statusBarOrientationChange:(NSNotification *)notification {
    
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    switch (interfaceOrientation) {
            
        case UIInterfaceOrientationUnknown:
            NSLog(@"未知方向");
            break;
            
        case UIInterfaceOrientationPortrait:
            NSLog(@"界面直立");
            break;
            
        case UIInterfaceOrientationPortraitUpsideDown:
            NSLog(@"界面直立,上下颠倒");
            break;
            
        case UIInterfaceOrientationLandscapeLeft:
            NSLog(@"界面朝左");
            break;
            
        case UIInterfaceOrientationLandscapeRight:
            NSLog(@"界面朝右");
            break;
        
        default:
            break;
    }
}

注意需要在dealloc移除监听
但是如果用户锁定屏幕方向后上面的方法时无法监听的
因此我们可以采用加速器来获取屏幕的方向
这里会用到
初始化

//sensitive 灵敏度
//目的是可以与系统的监听方法保持大概一致
static const float sensitive = 0.50;
- (CMMotionManager *)manager {
    if (!_manager) {
        _manager = [[CMMotionManager alloc] init];
        //更新的频率
        _manager.deviceMotionUpdateInterval = 0.3;
    }
    return _manager;
}

然后开始进行监听:

if (self.manager.deviceMotionAvailable) {
        NSLog(@"Device Motion Available");
        [self.manager startDeviceMotionUpdatesToQueue:[NSOperationQueue currentQueue]
                                      withHandler: ^(CMDeviceMotion *motion, NSError *error){
                                          [self performSelectorOnMainThread:@selector(handleDeviceMotion:) withObject:motion waitUntilDone:YES];
                                          
                                      }];
    } else {
        NSLog(@"No device motion on device.");
    }

监听后获取回调

- (void)handleDeviceMotion:(CMDeviceMotion *)deviceMotion {
    UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
    double x = deviceMotion.gravity.x;
    double y = deviceMotion.gravity.y;
    if (fabs(y) >= fabs(x)) {
        if (y >= 0) {
            // UIDeviceOrientationPortraitUpsideDown;
            NSLog(@"界面直立,上下颠倒");
        } else {
            // UIDeviceOrientationPortrait;
            NSLog(@"界面直立");
        }
    } else {
        if (x > sensitive) {
            // UIDeviceOrientationLandscapeRight;
            NSLog(@"界面朝右");
        } else if (fabs(x) > sensitive) {
            // UIDeviceOrientationLandscapeLeft;
            NSLog(@"界面朝左");
        } else {
            NSLog(@"界面直立");
        }
    }
}

注意我们需要在viewDidDisappear停止监听

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.manager stopDeviceMotionUpdates];
}

你可能感兴趣的:(iOS横竖屏转换记录)