美文网首页
iOS开发笔记 --- 屏幕旋转

iOS开发笔记 --- 屏幕旋转

作者: Rui_ai | 来源:发表于2019-10-14 18:08 被阅读0次

    一、关于方向的三种枚举

    1、UIDeviceOrientation

    UIDeviceOrientation 是硬件设备(iPhone、iPad等)本身的当前旋转方向,设备方向有7种(包括一种未知的情况),判断设备的方向是以 home键 的位置作为参照的。

    typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
        UIDeviceOrientationUnknown,        //未知
        UIDeviceOrientationPortrait,            //home键在下
        UIDeviceOrientationPortraitUpsideDown,  //home键在上
        UIDeviceOrientationLandscapeLeft,       //home键在右
        UIDeviceOrientationLandscapeRight,      //home键在左
        UIDeviceOrientationFaceUp,              //屏幕朝上
        UIDeviceOrientationFaceDown             //屏幕朝下
    } __TVOS_PROHIBITED;
    

    设备方向只能取值,不能设置,监测设备方向的变化,我们可以在 Appdelegate 文件中使用通知如下:

    if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
    }
    [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleDeviceOrientationDidChange:)
                                                     name:UIDeviceOrientationDidChangeNotification
                                                   object:nil];
    
    - (void)handleDeviceOrientationChange:(NSNotification *)notification{
        UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
        switch (deviceOrientation) {
            case UIDeviceOrientationPortrait:
                NSLog(@"home键在下");
                break;
            case UIDeviceOrientationPortraitUpsideDown:
                NSLog(@"home键在上");
                break;
            case UIDeviceOrientationLandscapeLeft:
                NSLog(@"home键在右");
                break;
            case UIDeviceOrientationLandscapeRight:
                NSLog(@"home键在左");
                break;
            case UIDeviceOrientationFaceUp:
                NSLog(@"屏幕朝上");
                break;
            case UIDeviceOrientationFaceDown:
                NSLog(@"屏幕朝下");
                break;
            case UIDeviceOrientationUnknown:
                NSLog(@"未知方向");
                break;
            default:
                NSLog(@"无法辨识");
                break;
        }
    }
    
    2、UIInterfaceOrientation

    App的界面方向,即应用里的某个界面展示方向(可以设置),可以理解为statusBar 所在的方向,是一个二维空间,有四个方向::

    typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
        //未知
        UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,   
        //home键在下
        UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
        //home键在上
        UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
        //home键在左
        UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
        //home键在右
        UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
    } __TVOS_PROHIBITED;
    
    3、UIInterfaceOrientationMask

    UIInterfaceOrientationMask 是ios6之后新增的一组枚举值。

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

    在iOS6之后,控制单个界面的旋转我们通常是下面三个方法来控制:

    //设置是否自动旋转
    - (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
    //设置当前界面支持的所有方向
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
    //设置进入界面默认支持的方向
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;
    

    二、设置应用支持屏转

    要想实现屏转功能,首先App得支持屏幕旋转。关于屏幕旋转的设置有很多,有Xcode的General设置,也有info.plist设置,还有代码设置等。但是这些其实都是在不同级别上实现旋转的设置,我们会遇到设置关闭旋转无效的情况,这就很可能是被上一级别控制的原因。

    控制屏幕旋转优先级为:工程Target属性配置(全局权限) = AppDelegate&&Window > 根视图控制器> 普通视图控制器。

    1、工程Target属性配置
    屏幕快照 2019-09-20 下午4.04.41.png
    2、Info.Plist设置
    屏幕快照 2019-09-20 下午9.06.37.png

    注意:Supported interface orientation里的设置和UIDevice Orientation的值一致的

    3、Appdelegate&&Window中设置
    - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
        return UIInterfaceOrientationMaskAll;
    }
    

    注意:如果我们实现了AppDelegate 的这一方法,那么我们的App的全局旋转设置将以这里的为准,即使前两种方法的设置与这里的不同。

    三、屏转的两种实现方式

    1、支持旋转的界面跟随用户手持设备旋转方向自动旋转
    • 设置应用支持屏转
    • 自定义TabBar控制器来设置屏幕的自动旋转
    //是否自动旋转
    -(BOOL)shouldAutorotate{
        return self.selectedViewController.shouldAutorotate;
    }
    
    //支持哪些屏幕方向
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        return [self.selectedViewController supportedInterfaceOrientations];
    }
    
    //默认方向
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
        return [self.selectedViewController preferredInterfaceOrientationForPresentation];
    }
    
    • 自定义导航控制器来设置屏幕的自动旋转
    //是否自动旋转
    -(BOOL)shouldAutorotate{
        return self.topViewController.shouldAutorotate;
    }
    
    //支持哪些屏幕方向
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        return [self.topViewController supportedInterfaceOrientations];
    }
    
    //默认方向
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
        return [self.topViewController preferredInterfaceOrientationForPresentation];
    }
    
    • 自定义基类控制器设置不支持自动转屏,并默认只支持竖屏
    //是否自动旋转:所有控制器默认不自动旋转,需要横屏的视图控制器中覆写此方法,返回YES
    -(BOOL)shouldAutorotate{
        return NO;
    }
    
    //支持哪些屏幕方向:只支持竖屏
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        return UIInterfaceOrientationMaskPortrait;
    }
    
    //默认方向:只支持正常竖屏
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
        return UIInterfaceOrientationPortrait;
    }
    
    • 对项目中需要转屏幕的控制器开启自动转屏、设置支持的旋转方向并设置默认方向
    2、单个界面强制旋转
    • 在Applegate文件中增加一个用于记录当前屏幕是否横屏的属性
    //记录当前界面是否支持横竖屏旋转
    @property (nonatomic, assign) BOOL allowAutoRotate;
    
    //此方法会在设备横竖屏变化的时候调用
    - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
        if (_allowAutoRotate) {
            //只支持横屏
            return UIInterfaceOrientationMaskLandscape;
        }else{
            //支持竖屏
            return UIInterfaceOrientationMaskPortrait;
        }
    }
    
    • 需要横屏的界面,进入界面后强制横屏,离开界面时恢复竖屏
    - (void)viewWillAppear:(BOOL)animated{
        [super viewWillAppear:animated];
        AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
        delegate.allowAutoRotate = YES;
        //进入界面:设置横屏
        [self setDeviceInterfaceOrientation:UIDeviceOrientationLandscapeLeft];
    }
    
    - (void)viewWillDisappear:(BOOL)animated{
        [super viewWillDisappear:animated];
        AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
        delegate.allowAutoRotate = NO;
        //离开界面:设置竖屏
        [self setDeviceInterfaceOrientation:UIDeviceOrientationPortrait];
    }
    
    //#MARK: 屏幕旋转
    //方法1和方法2只有在shouldAutorotate返回YES的时候生效
    //因为项目中未对shouldAutorotate覆写过,默认就是YES的
    //方法1:
    - (void)setInterfaceOrientation:(UIInterfaceOrientation)orientation {
        if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
            SEL selector = NSSelectorFromString(@"setOrientation:");
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
            [invocation setSelector:selector];
            [invocation setTarget:[UIDevice currentDevice]];
            int val = orientation;
            [invocation setArgument:&val atIndex:2];
            [invocation invoke];
        }
    }
    
    //方法2:
    - (void)setDeviceInterfaceOrientation:(UIDeviceOrientation)orientation {
        if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
            [[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:orientation] forKey:@"orientation"];
        }
    }
    
    

    四、可能会碰到的问题

    1、默认横屏无效的问题

    页面A竖屏,界面B需要进入时就显示横屏,从界面A到界面B中时,如果按照上面的第一种实现方式的话会遇到无法显示默认横屏的情况,因为没有旋转设备,shouldAutorotate就没被调用,也就没法显示我们需要的横屏,解决办法:

    //在需要默认横屏的界面里设置,进入时强制横屏,离开时强制竖屏
    - (void)viewWillAppear:(BOOL)animated{
        [super viewWillAppear:animated];
        //强制转屏
        NSNumber *orientationTarget = [NSNumber numberWithInt:UIDeviceOrientationLandscapeLeft];
        [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];
    }
    
    - (void)viewWillDisappear:(BOOL)animated{
        [super viewWillDisappear:animated];
        NSNumber *orientationTarget = [NSNumber numberWithInt:UIDeviceOrientationPortrait];
        [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];
    }
    
    2、旋转横屏后,状态栏不见了

    状态栏在横屏时被系统隐藏了,如不想横屏时被隐藏,可在基controller中作如下处理:

    //设置状态栏转屏时不隐藏
    - (BOOL)prefersStatusBarHidden {
        return NO;
    }
    

    相关文章

      网友评论

          本文标题:iOS开发笔记 --- 屏幕旋转

          本文链接:https://www.haomeiwen.com/subject/lgiructx.html