iOS强制横屏总结

作者: sfandy | 来源:发表于2016-07-28 14:29 被阅读21249次
    总体方向两点:
    • model下,present方式推出界面。
    • push横屏,带tabbar、navigation,且一个item下所有控制器对应的只有一个根navigation。

    接下来说说push方式强制旋转横屏时遇到的坑吧.....

    遇到的问题描述:
    • 横着,竖屏切换到横屏,是否“锁定竖屏”,都会偶尔造成无法旋转至横屏,iOS8表现较明显。
    • 横着或竖着,切换到横屏,挂起,再进入横屏,退出,再进入横屏,反复切换,偶尔会导致无法横屏,返回上竖屏时,界面返回了,但横屏无法返回。
    • 横竖屏来回切换,跳转,iOS8下,横屏控制器,竖屏控制器中init方法,横屏中viewWillDisapper、viewWillApper,调用顺序会乱,与iOS7、9执行的顺序不一样,个人感觉应该是push强制横屏或都是同一个navigation导致的原因吧。
    小总结下:
    • 由于该项目横屏中有来回跳转,到处跳转的,一个item下对应的根控制下又只能是同一个navigation,分享模块又是基于tabbar上的navigation推出的,分享模块无回调,导致我瞻前顾后的,一直盯着push方式到底是否可以强制旋转屏幕........百般绞尽脑汁的研究

    • 思来想去的,就参考了爱奇艺横屏播放视频的方式,横屏中的跳转都先回到竖屏播放控制器中跳转,相当于横屏中跳转多了一层过渡控制器;竖屏返回,应该是返回根控制器,所以,横屏应该是一个新的navigation,而分享都是在横屏中处理的。而我这项目中,分享的列表是在tabbar上的navigation加在windows上的,分享跳转都是基于tabbar的navigation,故无法再新建navigation,否则会引发一系列问题,比如无法跳转至首页,基本上各种在横屏里面的老代码跳转方式都不行了。

    故此,我又回到全部push方式继续坑........

    因为我也不想啊,如果用present方式涉及改动的代码模块太多了.....

    其实有点不明白的是,产品为何如此设计:横屏和竖屏,全模块之间的跳转,试问这样真的好吗.........

    接下来分析下push方式,仅限于参考和积累问题吧:

    解决方案:

    先说下思路吧:

    1.实时的更新当前应用所支持的方向,手动调用方法-    (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window;
    2.把当前控制器的方向给到当前navigation,一定要保证统一,不能乱套,否则会导致界面来回跳转错乱的问题;
    3.及时刷新当前控制器,手动调用方法[UIViewController attemptRotationToDeviceOrientation]。
    

    一、 present方式:
    就不多说了,调用系统的三个方法,基本上没什么问题。

    二、push方式:

    • 基类tabbar代码:
      #pragma mark - - orientation
      // 是否支持转屏
      - (BOOL)shouldAutorotate
      {
      return [self.selectedViewController shouldAutorotate];
      }
      // 返回nav栈中的最后一个对象支持的旋转方向
      - (UIInterfaceOrientationMask)supportedInterfaceOrientations
      {
      return [self.selectedViewController supportedInterfaceOrientations];
      }
      // 返回nav栈中最后一个对象,坚持旋转的方向
      - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
      {
      return [self.selectedViewController preferredInterfaceOrientationForPresentation];
      }

    • 基类navigation代码:
      //旋转方向 默认竖屏
      @property (nonatomic , assign) UIInterfaceOrientation interfaceOrientation;
      @property (nonatomic , assign) UIInterfaceOrientationMask interfaceOrientationMask;

      #pragma mark - - orientation
      //设置是否允许自动旋转
      - (BOOL)shouldAutorotate {
          return YES;
      }
      
      //设置支持的屏幕旋转方向
      - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        return self.interfaceOrientationMask;
      }
      
      //设置presentation方式展示的屏幕方向
      - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        return self.interfaceOrientation;
      }
      
    • 基类BaseViewController代码:
      - (void)viewDidLoad
      {
      [super viewDidLoad];
      [UIViewController attemptRotationToDeviceOrientation];
      }

    • AppDelegate代码
      @property (assign , nonatomic) BOOL isForceLandscape;
      @property (assign , nonatomic) BOOL isForcePortrait;

      -(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
        if (self.isForceLandscape) {
            return UIInterfaceOrientationMaskLandscape;
        }else if (self.isForcePortrait){
            return UIInterfaceOrientationMaskPortrait;
        }
        return UIInterfaceOrientationMaskPortrait;
      }
      
    • 横屏viewController代码:
      重点说明:
      1.此处需要手动调用
      - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window;
      且设置当前 应用只支持横屏;
      因为,该方法在viewWillAppear之后执行的。
      2.更新了支持的方向后,记得刷新下控制器,调用:
      [UIViewController attemptRotationToDeviceOrientation];
      否则,[UIScreen mainScreen].bounds的size不是你期望的;self.view.frame/bounds都不是期望的。
      3.其实网上说的一些普通的方式实现横屏,问题多,更多的是因为self.view.frame/bounds/[UIScreen mainScreen].bounds没有及时更新的。

      - (void)viewWillAppear:(BOOL)animated
        {
          [super viewWillAppear:animated];
          //强制旋转竖屏
          [self forceOrientationLandscape];
          CKNavigationController *navi = (CKNavigationController *)self.navigationController;
          [self.navigationController setNavigationBarHidden:YES animated:animated];
          navi.interfaceOrientation =   UIInterfaceOrientationLandscapeRight;
          navi.interfaceOrientationMask = UIInterfaceOrientationMaskLandscapeRight;
      
          //强制翻转屏幕,Home键在右边。
          [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) forKey:@"orientation"];
          //刷新
          [UIViewController attemptRotationToDeviceOrientation];
      }
      

      重点说明:
      1.离开横屏时、横屏中跳转,记得强制旋转至竖屏;
      2.如果没有及时旋转至横屏,会导致[UIScreen mainScreen].bounds没有及时更新,从而影响其它模块的布局问题;
    
     - (void)viewWillDisappear:(BOOL)animated 
      {
        [super viewWillDisappear:animated];
         //强制旋转竖屏
        [self forceOrientationPortrait];
        CKNavigationController *navi = (CKNavigationController *)self.navigationController;
        navi.interfaceOrientation = UIInterfaceOrientationPortrait;
        navi.interfaceOrientationMask = UIInterfaceOrientationMaskPortrait;
    
         //设置屏幕的转向为竖屏
        [[UIDevice currentDevice] setValue:@(UIDeviceOrientationPortrait) forKey:@"orientation"];
        //刷新
        [UIViewController attemptRotationToDeviceOrientation];
       }
    

      #pragma  mark 横屏设置
      //强制横屏
    - (void)forceOrientationLandscape
      {
        CKAppDelegate *appdelegate=(CKAppDelegate *)[UIApplication sharedApplication].delegate;
        appdelegate.isForceLandscape=YES;
        appdelegate.isForcePortrait=NO;
        [appdelegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.view.window];
      }
    
      //强制竖屏
     - (void)forceOrientationPortrait
      {
        CKAppDelegate *appdelegate=(CKAppDelegate *)[UIApplication sharedApplication].delegate;
        appdelegate.isForcePortrait=YES;
        appdelegate.isForceLandscape=NO;
        [appdelegate application:[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.view.window];
      }
    
    - (BOOL)prefersStatusBarHidden{
        return YES;
    }
    
    感受
    • 网上找了很多种方法,关于
      这个方法有人说上架可能会被拒,虽然是间接调用私有方法。
      但这个方法经过多轮测试,其实不是有用的,不靠谱,只要按照我上面说的步骤测试,绝对会有bug的,故此不建议大家使用该方法。

       - (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
        if([[UIDevicecurrentDevice] respondsToSelector:@selector(setOrientation:)]) {        
            SEL selector  =NSSelectorFromString(@"setOrientation:");
            NSInvocation*invocation = [NSInvocationinvocationWithMethodSignature:[UIDeviceinstanceMethodSignatureForSelector:selector]];     
            [invocation setSelector:selector];        
            [invocation setTarget:[UIDevicecurrentDevice]];
            intval = orientation;
            // 从2开始是因为0 1 两个参数已经被selector和target占用
            [invocation setArgument:&val atIndex:2];                                                     
            [invocation invoke];   
        }}
      

    以上所述,支持iOS7、8、9系统,经iphone \iPod测试过,但貌似iOS8从横屏挑战至其它竖屏的界面,偶尔会有问题,概率很小,不影响。
    原因:可能是iOS8 SDK与其它不同吧。

    更新iOS8的问题:

       iOS8-8.4,横屏跳转至其它界面,最好延迟跳转,不然其它界面的viewWillApper会比横屏的viewWillDisApper先执行,导致无法旋转回来。
       CGFloat timef = 0.8;
        //主要是转屏之后view的再次旋转
        if (kSystemVersion>8||kSystemVersion<8.4) {
            self.view.hidden = YES;
            [self viewWillDisappear:NO];
            timef = 0.1;
        }
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, timef*NSEC_PER_SEC);
        dispatch_after(time, dispatch_get_main_queue(), ^{
            dispatch_async(dispatch_get_main_queue(), ^(){
               //跳转界面
            });
        });
    

    参考资料:
    http://www.cocoachina.com/bbs/read.php?tid=244119

    相关文章

      网友评论

      • 不辣先生:横屏的时候通知从侧面出来,有什么解决方案,大神
      • 不辣先生:横屏下如何强制隐藏tabbar?
      • Funnyer:我只是调用了这两句代码,就可以达到效果了://设置屏幕的转向为竖屏
        [[UIDevice currentDevice] setValue:@(UIDeviceOrientationPortrait) forKey:@"orientation"];
        //刷新
        [UIViewController attemptRotationToDeviceOrientation];
        Funnyer:@快乐的一直小青蛙 每次强制变换要记得刷新:[UIViewController attemptRotationToDeviceOrientation];
        快乐的一直小青蛙:很棒棒诶,找了很多方法,没想到用你的两句代码就可以搞定
        Funnyer:但是反复进入需要横屏的webView,[UIDevice currentDevice].orientation = UIDeviceOrientationUnknown,怎么回事?
      • zcz:感谢楼主,完美的解决我的问题:+1:
        sfandy:@zcz :+1:
      • Rhythm_Ting:在iOS11 下,从横屏回到竖屏,tabbar 上的图标全都挤到一起去了,可能是什么原因引起的呢 ? 其他版本的系统没影响
      • timeforasong:好,找了很多办法,这是唯一能用的!
      • 假能干:老哥~按照上面的代码,我试着写了一下~ 已经实现push指定界面横屏~ 回到上界面不影响上界面布局, 目前出现唯一问题为:横屏界面布局失败,布局frame位置为竖屏适配。
        在viewWillAppear方法中手动调用了老哥你的forceOrientationLandscape(强制横屏)方法
        而后在viewWillAppear方法中刷新:
        [UIViewController attemptRotationToDeviceOrientation];
        但是未能实现横屏的布局~
        想到了,回到竖屏成功可能是因为,根本就没有将布局转为横屏,所以未能影响竖屏的布局。
        老哥~这种问题方便帮我分析一下么~
      • 叫我困兽:按照你的方法,控制器可以横屏。但是我想在横屏的控制器中增加子控制器,自控制器使用自定义初始化方法,来设置这个子控制view的fram出现了问题。fram的size完全对不上.....不是横竖颠倒,而是都是错误的,请问你遇到这这个问题么?是哪里错了?
        叫我困兽:知道问题了,是因为viewDidLayoutSubviews时候重新绘制frame了。要是修改子控制的view需要在这里绘制。就好了
      • 5ec1da87f063:看到最后,其实这个方法并不靠谱吗?....使用私有api,不是每次都管用.是这样吗?
        sfandy:@5ec1da87f063 私有API不能使用。
        sfandy:@5ec1da87f063 自己看清楚,不会用到私有api
      • 尕宝11:@秘制鸭腿 我项目需求只有播放也可以全屏,我的全屏是手动修改约束实现的,在横屏滑动修改音量的时候,音量的提示框还是竖屏的样子,该怎么解决啊?很急
        秘制鸭腿:我是强制横屏的,你可以尝试一下、只是偶尔会出现bug。
      • 三分慢先森:在A控制器中播放视频手机横屏,然后pop至B控制器,手机仍然横着放,但是要让B控制器屏幕转换为竖屏状态,此过程中supportedInterfaceOrientations方法成功返回了只支持竖屏的状态,屏幕不改变,然后调用attemptRotationToDeviceOrientation方法,屏幕仍不改变,请问怎么解决呢
      • ac6ff43241d6:转屏的时候,报内存警告,崩了,怎么解决???有谁遇到没???
      • ac6ff43241d6:用的是 UIImagePickerController 选择相册图片 pressent 的时候崩的,说方向不对,这个时候怎么刷新方向呢》????
      • 秘制鸭腿:present出来的控制器偶发性的不旋转, 下面一块是黑的, 返回后也是错乱的, 但是按了home键再进入即可恢复, 请问有什么好的解决办法吗
        bdada38a6732:我也出现了这个问题,请问解决了吗
        秘制鸭腿:@依稀一米 暂时无法解决, 我发现很多应用有类似情况 , 建议将返回键放左边, 至少用户能够返回上一级
        ec202b212cc6:我也遇到了这个问题,请问你解决了吗?
      • 吴德馨:前辈 能否把 demo 发出来,我试着写了一遍,但是还是没有达到效果
        我爱WSP:我在横屏的时候强制成竖屏,然后把手机调整到横屏这时候在点横屏按钮的时候获取device的方向还是之前设置的竖屏。。。楼主可知道原因。
        sfandy:@请问一个月 只提供思想,自己琢磨琢磨更靠谱些的。
      • codeing小牛:用作者的方法成功的解决了问题,不过底层是tababar 要在viewdidload 里选择一个默认的控制器 navigationcon 里viewdidload 方法里也要设置默认的支持方向和偏好方向否则就会报错
        ac6ff43241d6:@codeing小牛 现在这种方法,转屏的时候有时会卡,还有可能崩,你是怎么解决的???
        codeing小牛:@落翼之殇_8050 self.selectedIndex = 0 ; self 为自定制的tabar 对象 0 是默认选择第一个控制器 依次类推
        ac6ff43241d6:不过底层是tababar 要在viewdidload 里选择一个默认的控制器 ?? 怎么选择呢??
      • 下雨就好:博主,我按照你这么弄了,但是这个会导致我的原来的登陆页面直接崩溃,崩溃信息为-
        *** Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'preferredInterfaceOrientationForPresentation must return a supported interface orientation!'
        *** First throw call stack:
        (
        0 CoreFoundation 0x0000000114e69e65 __exceptionPreprocess + 165
        1 libobjc.A.dylib 0x000000011452ddeb objc_exception_throw + 48
        2 CoreFoundation 0x0000000114e69d9d +[NSException raise:format:] + 205
        3 UIKit 0x0000000115b19199 -[UIViewController _preferredInterfaceOrientationForPresentationInWindow:fromInterfaceOrientation:] + 691
        4 UIKit 0x00000001162a56ce -[_UIFullscreenPresentationController _adjustOrientationIfNecessaryInWindow:forViewController:preservingViewController:] + 374
        5 UIKit 0x0000000115acc5d9 -[UIPresentationController _dismissWithAnimationController:interactionController:target:didEndSelector:] + 622
        6 UIKit 0x0000000115b128ee -[UIViewController _dismissViewControllerWithAnimationController:interactionController:completion:] + 569
        7 UIKit 0x0000000115b124aa -[UIViewController _dismissViewControllerWithTransition:from:completion:] + 995
        8 UIKit 0x0000000115b11a8c -[UIViewController dismissViewControllerWithTransition:completion:] + 1273
        9 UIKit 0x0000000115b117f3 -[UIViewController dismissViewControllerWithTransition:completion:] + 608
        10 UIKit 0x0000000115b10f2c -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 489
        11 UIKit 0x0000000115b144f1 -[UIViewController dismissViewContro
        sfandy:@下雨就好 对应的方向不对啊 ,preferredInterfaceOrientationForPresentation must return a supported interface orientation!'
      • 7e70e3784a48:你好,我用了你的方法成功实现了push的横屏。但是present出来的界面在dismiss时就会报错。不知道该怎么解决呢。应该是基类navigation抛出的错误
        sfandy:@flyiron :+1:
        7e70e3784a48:解决了,在基类navigationcontroller里的preferredInterfaceOrientationForPresentation和supportedInterfaceOrientations方法中判断self.topViewController是否是你要找的那个class,返回 [self.viewControllers.lastObject preferredInterfaceOrientationForPresentation];

        谢谢楼主!
        7e70e3784a48:present出来的界面是竖屏的uiimagepickerview,这个view是继承自uinavigationcontroll的,所以不能push
      • 小弱鸡:我在使用你的方法做强制横屏时,手动锁屏操作,然后返回上一个页面,UI位置完全乱套,有什么方案解决么?
        sfandy:@DeepWater 检查下对应的navigation方向是否对应了吧
      • 47200923d724:[self forceOrientationPortrait];
        这个是什么方法啊 楼主~~~我也遇到强制横竖屏的问题了~~~
        47200923d724:@sfandy
        楼主,你有没有试过模拟测试的时候,一进入app,本来是竖屏的,但是显示出来是横着的屏幕,竖着的画面,然后下面那一板块是黑的
        47200923d724:@sfandy 嘿嘿😜不好意思 我再斟酌斟酌~~~
        sfandy:@lin111111111 看下面好吧 :sweat:
      • 46684f2a32ec:大神,我用了你的方法,实现手动控制横竖屏了,现在问题来了,我在某个控制器中模态弹出系统的相片选择控制器,选择完,走到dismiss方法的时候就崩溃了,请教哈怎么解决,或者模态弹出一个系统导航包裹的控制器,dismiss的时候也会崩溃*** Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'preferredInterfaceOrientationForPresentation must return a supported interface orientation!'
        46684f2a32ec:@liurang_major 崩溃的界面也不需要旋转屏幕,就是竖屏,大神,帮帮我指导下,比较急....
        sfandy:@liurang_major 你对应支持的旋转方向不对啊,没对应的啊。
        46684f2a32ec:大神,比较急,留个qq号,好联系你
      • 峰子1994:有demo吗
      • dd25f9257b81:谢谢 参考你的解决了自己的问题.
        sfandy:@AlaskuNull :+1:
      • lhq_cd:请教一下,我只想让我的App其中某个界面横屏,其他都是竖屏,应该怎么做呢,我用了你说的那两个强制横屏的方法,但是没用啊 ···是因为我没有勾选那个设置里需要支持横屏吗?但如果勾选了,不用实现方法也能实现横屏,但是这样所有的界面都可以横屏了啊?
        sfandy:@coderHQLee - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window; 这个方法就替换了勾选的操作了。
        sfandy:@coderHQLee 看看你的代码吧。这种方式就是处理某个界面横屏,其它竖屏问题的。

      本文标题:iOS强制横屏总结

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