iOS屏幕旋转与锁屏

在做视频开发时遇到屏幕旋转问题,其中涉及到 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 ,有兴趣的朋友欢迎来探讨。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,198评论 6 514
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,334评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,643评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,495评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,502评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,156评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,743评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,659评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,200评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,282评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,424评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,107评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,789评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,264评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,390评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,798评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,435评论 2 359

推荐阅读更多精彩内容