iOS屏幕旋转与锁屏

作者: 千江月_Chen | 来源:发表于2018-07-11 11:36 被阅读95次

    在做视频开发时遇到屏幕旋转问题,其中涉及到 StatusBar、 UINavigationController、UITabBarController 、UIViewcontroller

    在设备锁屏下的整体效果图

    iOS-旋转.gif

    主要涉及以下4点:

    • 横竖屏的旋转
    • 屏幕旋转相应改变视图位置
    • 旋转时状态栏的隐藏与显示
    • 锁屏

    1、横竖屏旋转

    • 第1步:

       -(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
      
      //    NSLog(@"0000000---------%@",NSStringFromClass([[self topViewController] class]));
      //    if ([NSStringFromClass([[self topViewController] class]) isEqualToString:@"FirstViewController"]) {
      //        //横屏
      //        return UIInterfaceOrientationMaskLandscapeRight;
      //    }
      //    //竖屏
      //    return UIInterfaceOrientationMaskPortrait;
          
          NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;
      
          if(self.window.rootViewController){
              //取出当前显示的控制器
              UIViewController *presentedViewController = [self topViewControllerWithRootViewController:self.window.rootViewController];
              //按当前控制器支持的方向确定旋转方向(将旋转方向重新交给每个控制器自己控制)
              NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
              orientations = [presentedViewController supportedInterfaceOrientations];
          }
      
          return orientations;
      }
      //获取界面最上层的控制器
      //- (UIViewController*)topViewController {
      //    NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
      //    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
      //}
      //一层一层的进行查找判断
      - (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
          NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
          if ([rootViewController isKindOfClass:[UITabBarController class]]) {
             
              UITabBarController* tabBarController = (UITabBarController*)rootViewController;
              NSLog(@"Tabbar:%@",NSStringFromClass([tabBarController.selectedViewController class]));
      
              return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
          } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
              
              UINavigationController* nav = (UINavigationController*)rootViewController;
              NSLog(@"nav:%@",NSStringFromClass([nav.visibleViewController class]));
              return [self topViewControllerWithRootViewController:nav.visibleViewController];
          } else if (rootViewController.presentedViewController) {
              NSLog(@"present:%@",NSStringFromClass([rootViewController.presentationController class]));
              UIViewController* presentedViewController = rootViewController.presentedViewController;
              return [self topViewControllerWithRootViewController:presentedViewController];
          } else {
              NSLog(@"root:%@",rootViewController);
              return rootViewController;
          }
      }
      
      

    代码中通过 -(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window 方法将控制器交给自己控制,该方法默认值为Info.plist中配置的Supported interface orientations项的值。

    • 第2步:在各控制器设置支持的方向

      //是否允许旋转(默认允许)
      - (BOOL)shouldAutorotate {
          return YES;
      }
      
      - (UIInterfaceOrientationMask)supportedInterfaceOrientations{
          //允许旋转的方向
          return UIInterfaceOrientationMaskAll;
      }
      

      其中- supportedInterfaceOrientations 方法在 iPad 中默认取值为 UIInterfaceOrientationMaskAll,即默认支持所有屏幕方向;而 iPhone 跟 iPod Touch 的默认取值为 UIInterfaceOrientationMaskAllButUpsideDown,即支持除竖屏向下以外的三个方向。
      在设备屏幕旋转时,系统会调用 - shouldAutorotate 方法检查当前界面是否支持旋转,只有 - shouldAutorotate返回 YES 的时候,- supportedInterfaceOrientations 方法才会被调用,以确定是否需要旋转界面。
      这个是 TabbarController 中设置的,它会影响关联的 UIViewController 的支持方向,需要在 UIViewController 中进一步设置

      //此方法来控制能否横竖屏 控制锁屏
        - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
            NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
            UIInterfaceOrientationMask inter;
            if (_lockScreen) {
                switch (_lockOrientation) {
                    case 1:
                        inter = UIInterfaceOrientationMaskPortrait;
                        break;
                    case 2:
                        inter = UIInterfaceOrientationMaskPortraitUpsideDown;
                        break;
                    case 3:
                        inter = UIInterfaceOrientationMaskLandscapeRight;
                        break;
                    case 4:
                        inter = UIInterfaceOrientationMaskLandscapeLeft;
                        break;
                    default:inter = UIInterfaceOrientationMaskAll;
                        break;
                }
            } else {
                inter = UIInterfaceOrientationMaskAll;
            }
            //支持全部方向
            return inter;
        }
      
    • 第3步:强制转换控制器方向

      - (void)setInterOrientation:(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;
                // 从2开始是因为0 1 两个参数已经被selector和target占用
                [invocation setArgument:&val atIndex:2];
                [invocation invoke];
            }
       }
      

      这样就可以完成横竖屏的切换。

    2、屏幕旋转相应改变视图位置

    这里先扩展UIDeviceOrientation & UIInterfaceOrientation的知识

    • UIDeviceOrientation 设备的物理方向
      UIDeviceOrientation即我们手持的移动设备的Orientation,是一个三围空间,有六个方向,通过[UIDevice currentDevice].orientation获取当前设备的方向。

      typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
          UIDeviceOrientationUnknown,
          UIDeviceOrientationPortrait,            
          UIDeviceOrientationPortraitUpsideDown,  // Device oriented vertically, home button on the top 竖屏向下,即头在下,Home 键在上
          UIDeviceOrientationLandscapeLeft,       // Device oriented horizontally, home button on the right 横屏头在左,Home键在右
          UIDeviceOrientationLandscapeRight,      // Device oriented horizontally, home button on the left  横屏头在右,Home键在左
          UIDeviceOrientationFaceUp,              // Device oriented flat, face up
          UIDeviceOrientationFaceDown             // Device oriented flat, face down
      } ;
      
    • UIInterfaceOrientation界面的显示方向
      UIInterfaceOrientation即我们看到的视图的Orientation,可以理解为statusBar所在的方向,是一个二维空间,有四个方向, 通过[UIApplication sharedApplication].statusBarOrientation即状态栏的方向获取当前界面方向。

      // Note that UIInterfaceOrientationLandscapeLeft is equal to   UIDeviceOrientationLandscapeRight (and vice versa).
      // This is because rotating the device to the left requires rotating the content to the right.
      typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
          UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
          UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
          UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
          UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
          UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
      } 
      
    • UIInterfaceOrientationMask支持的方向

      // iOS 6 之后用于控制界面的枚举值
      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),
      }
      

    由上可以发现:

    1. iOS 6 及之后版本使用的UIInterfaceOrientationMask类型来控制屏幕屏幕方向,该类型也新增加了几个枚举取值,可用一个枚举取值来代表多个屏幕方向,使用起来更方便。
    2. 注意在UIInterfaceOrientation中有注释 Note that UIInterfaceOrientationLandscapeLeft is equal to UIDeviceOrientationLandscapeRight (and vice versa).
      This is because rotating the device to the left requires rotating the content to the right
      ,大意是界面的左转相当于设备的右转,如果设备向左转时就需要内容(即界面)向右转。即:
      UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight
      UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
      下面还会举例说明。

    其实UIDeviceOrientationUIInterfaceOrientation是两个互不相干的属性,通常情况下会一起出现,在这里正好利用此特性在屏幕旋转后进行重新布局。

    • 第1步:监听 UIDeviceOrientationDidChangeNotification状态

      //监听设备旋转 改变 视图 对应位置
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange) name:UIDeviceOrientationDidChangeNotification object:nil];
      
      //用来控制横竖屏时调整视图位置
      - (void)deviceOrientationDidChange
      {
          [self isPortrait]; 
      }
      
    • 第2步:重新布局

      if (_interOrientation == UIInterfaceOrientationPortrait || _interOrientation == UIInterfaceOrientationPortraitUpsideDown) {
                self.top.constant = 145;
                self.bottom.constant = 210;
                
            } else if (_interOrientation == UIInterfaceOrientationLandscapeRight || _interOrientation == UIInterfaceOrientationLandscapeLeft) {
                self.top.constant = 40;
                self.bottom.constant = 50;
            }
      

    例如:竖屏转横屏
    界面竖屏UIInterfaceOrientationPortrait ->横屏UIInterfaceOrientationLandscapeRight,设备方向UIDeviceOrientationPortrait->UIDeviceOrientationLandscapeLeft,在设备发生变化这个过程触发UIDeviceOrientationDidChangeNotification监听,然后进行重新布局。

    3、旋转时状态栏的隐藏与显示

    • 这里只记述旋转时状态栏的变化,由竖屏想横屏变化时状态栏会消失。

      //在需要的`UIViewController`设置是否隐藏
      - (BOOL)prefersStatusBarHidden {
        NSLog(@"%s, line = %d",__FUNCTION__,__LINE__);
        return NO;
      }
      

    4、锁屏

    锁屏时,不管系统锁屏是否关闭、Push 或 Present 返回后,界面依然保持不变。

    • 第1步:设置锁屏

      - (IBAction)lockAction:(UIButton *)sender {
            if (_lockScreen) {
                
                _lockScreen = NO;
                [sender setTitle:@"锁定屏幕" forState:UIControlStateNormal];
            } else {
                _lockScreen = YES;
                
                [sender setTitle:@"解开屏幕" forState:UIControlStateNormal];
            }
            _lockOrientation = _interOrientation;
        }
      
    • 第2步:绕过强转

      - (void)interfaceOrientation:(UIInterfaceOrientation)orientation
        {
           
            [self isPortrait];
            //锁屏情况下 不旋转
            if (!_lockScreen) {
                [self setInterOrientation:orientation];
            }
      
    • 第3步:针对 Push 或 Present 返回后

      - (void)viewWillAppear:(BOOL)animated {
            
            if (_lockScreen) {
                //记录返回时的界面状态
                [self setInterOrientation:_lockOrientation];
            } else {
              [self isPortrait];
            }
        }
      

    5、 针对特定UIViewController方向的支持

    -(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
     
          if ([NSStringFromClass([[self topViewController] class]) isEqualToString:@"FirstViewController"]) {
              //横屏
              return UIInterfaceOrientationMaskLandscapeRight;
          }
          //竖屏
          return UIInterfaceOrientationMaskPortrait;
      }
    

    最后的献上GitHub代码,还有2个小的 bug ,有兴趣的朋友欢迎来探讨。

    相关文章

      本文标题:iOS屏幕旋转与锁屏

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