美文网首页征服iOSiOS精华文章iOS 日常开发手册
[iOS]代码控制APP中视图横屏/竖屏切换

[iOS]代码控制APP中视图横屏/竖屏切换

作者: 流火绯瞳 | 来源:发表于2016-05-17 11:30 被阅读11190次

    很多时候,项目中都有这样的需求:APP中以竖屏为主,个别界面会要求横屏显示,或者要根据用户的手机朝向自动切换横竖屏;下面就来详细讲解,在项目中怎么使用代码来控制APP的界面转换.
    首先,要想APP支持多个方向,需要在工程进行设置支持的朝向:
    General-->Deployment Info-->Device Orientation中进行设置

    设置APP支持的手机方向

    这样,就可以在项目中使用代码来控制页面的朝向了,在这之前,需要知道控制视图是否能够自动旋转,以及支持的朝向,是通过哪些方法来控制的?
    其实,主要使用的是下面三个方法:

    // New Autorotation support.  
    //是否自动旋转,返回YES可以自动旋转  
    - (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
    //返回支持的方向  
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
    // Returns interface orientation masks.  
    //这个是返回优先方向  
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) __TVOS_PROHIBITED;  
    

    一般情况下,实现前两个方法即可!这些都是UIViewController的实例方法,直接在需要设置的控制器内重写上面的方法即可;
    有时候,你会发现,只有在APP启动时出现的第一个界面内重写上面的两个方法,会有效果;而在其他的界面重写,并没有效果.这是因为,程序启动后的第一个界面是程序的跟视图控制器,而这些设置,必须是影响了跟视图中的相应设置,才会有效果;
    假如,你在跟视图中进行了如下设置:

    - (BOOL)shouldAutorotate{  
        return NO;  
    }  
    

    那么,在整个项目中,无论你如何设置,都不会有自动旋转屏幕的效果.
    综上可知,如果要改变某个视图屏幕方向,需要告诉跟视图,在跟视图中修改为相应的设置即可;
    所以,问题来了,怎么告诉跟视图呢?这要视情况而定:

    1.当前viewController就是跟视图控制器

    这种情况最简单,直接在控制器中重写上面的方法,设置是否支持自动旋转,以及支持的方向即可:

    - (BOOL)shouldAutorotate{
        return YES;
    }
    
    #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
    - (NSUInteger)supportedInterfaceOrientations
    #else
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations
    #endif
    {
        return UIInterfaceOrientationMaskAll;
    }
    

    像上面我设置为支持自动旋转,支持所有的朝向;
    这种方式, 切换视图的方式主要是模态(present), 只需要在相应的UIViewController里重写上面的方法即可.
    但是这种情况很少见...
    对应的demo为:LZInterfaceRotation中的Rotation-UIViewController

    2.项目的跟视图控制器是导航(UINavigationController)

    这种情况比较常见,也很简单;一般使用的导航的时候,都不会去直接使用系统的UINavigationController,而会定义一个他的子类,做一些公共的设置:

    #import <UIKit/UIKit.h>
    
    @interface BaseNaviViewController : UINavigationController
    
    @end
    

    然后在导航的viewController里面重写上面的三个方法,这里也有两种方式来控制:

    2.1 设置最后一个push的viewController

    这种方式,主要是用在,当push到某个视图控制器时,要求他能够支持自动旋转,或者强制转为横屏;这里主要用到了导航的viewControllers属性,这是一个数组,数组的最后一个元素,就是最后push进去的viewController,所以,实现代码如下:

    -(BOOL)shouldAutorotate  
    {  
        return [[self.viewControllers lastObject] shouldAutorotate];  
          
    }  
      
    #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0  
    - (NSUInteger)supportedInterfaceOrientations  
    #else  
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations  
    #endif  
    {  
         return [[self.viewControllers lastObject] supportedInterfaceOrientations];  
    }  
      
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {  
          
        return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];  
    }  
    

    到这里还没有结束,这里只是获取到了最后一个push的viewController,然后用它去调用了相应的方法,具体的方法实现,还是需要在哪个viewController里进行设置的,也就是在需要特殊设置的viewController里重写上面的方法:

    -(BOOL)shouldAutorotate
    {
        return YES;
    }
    
    #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
    - (NSUInteger)supportedInterfaceOrientations
    #else
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations
    #endif
    {
        return UIInterfaceOrientationMaskPortraitUpsideDown;
    }
    
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        
        return UIInterfaceOrientationPortraitUpsideDown;
    }
    

    其实这样也就是告诉跟视图控制器,这个界面我要特殊设置一下;
    对应的demo为: LZInterfaceRotation中的Rotation-UINavigationController

    2.2设置指定的控制器

    第一种方法是,只要push进去一个视图控制器,都可以进行相应的设置,而这种方法是只针对特定的控制器进行设置,例如这里,我需要把SecondViewController单独设置为支持横屏,只需要将第一种方法中的导航里的相应代码做如下修改:

    -(BOOL)shouldAutorotate
    {
        if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
            return YES;
        }
        
        return NO;
    }
    
    #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
    - (NSUInteger)supportedInterfaceOrientations
    #else
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations
    #endif
    {
        if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
                    return UIInterfaceOrientationMaskLandscapeLeft;
                }
    
        return UIInterfaceOrientationMaskPortrait;
    }
    
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
        
        if ([[self.viewControllers lastObject]isKindOfClass:[SecondViewController class]]) {
            return UIInterfaceOrientationLandscapeLeft;
        }
    
        return UIInterfaceOrientationPortrait;
    }
    

    这样的好处是,可以控制指定的视图控制器,也不用在指定的视图控制器内再重写上面的方法;
    坏处就是,如果需要控制的视图控制器比较多的话,加的判断会多很多;没有方式一灵活;
    对应的demo为: LZInterfaceRotation中的Rotation-UINavigationController副本

    3.跟视图控制器是UITabbarController

    这种情况比较常见,项目的跟视图控制器是一个UITabBarController,这种情况的实现相对来说要复杂一些,但是,明白了原理,总是有方法来实现的.归根结底,也就是告诉跟视图,我这个界面需要支持旋转了;
    由于,在UITabBarController里直接获取到UIViewController比较困难,所以这里我采用通知的形式,来获取相应的设置状态;
    首先,设置一个全局的BOOL值,用于接收通知发送的参数:

    #import <UIKit/UIKit.h>
    
    @interface BaseTabBar : UITabBarController
    {
        BOOL shouldAutorotate;  
    }
    @end
    

    然后注册一个通知:

    //注册旋转屏幕的通知
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(autorotateInterface:) name:@"InterfaceOrientationNotification" object:nil];
    

    实现通知方法:

    -(void)autorotateInterface:(NSNotification *)notifition
    {
        shouldAutorotate = [notifition.object boolValue];
    }
    

    然后在重写的方法里加入判断:

    /** 
     * 
     *  @return 是否支持旋转 
     */  
    -(BOOL)shouldAutorotate  
    {  
        NSLog(@"======%d",shouldAutorotate);  
       return shouldAutorotate;
    }  
      
    /** 
     *  适配旋转的类型 
     * 
     *  @return 类型 
     */  
    -(UIInterfaceOrientationMask)supportedInterfaceOrientations  
    {  
        if (!shouldAutorotate) {  
            return UIInterfaceOrientationMaskPortrait;  
        }  
        return UIInterfaceOrientationMaskAllButUpsideDown;  
    }  
    

    这里我直接将支持的方向写死了,只是判断了一下是否支持自动旋转屏幕,如果需要将支持的方向传过来,可以修改通知携带的参数;
    最后在需要自动转屏的控制器内发送通知:

    [[NSNotificationCenter defaultCenter] postNotificationName:@"InterfaceOrientationNotification" object:@"YES"]; 
    

    对应的demo为: LZInterfaceRotation中的Rotation-UITabBarController

    PS新增: 关于何时发送通知
    例如有这样的需求: A竖屏, 不能自动旋转, B 可横屏可竖屏可自动旋转, 而且在从B回到A的时候, 如果不是竖屏, 强制转为竖屏, 这就用到了下面强制转屏的方法;
    我们在使用的时候肯定是在BviewWillAppear方法里发送通知告诉跟视图要自动旋转, 而在离开这个视图时, 就有可能会在viewWillDisappear方法里发送不能自动旋转的通知, 这样是不能实现效果的, 应该放在viewDidDisappear方法里发送;
    为严谨起见, 建议在viewDidAppear方法里发送可以自动旋转的通知, 在viewDidDisappear方法里发送不可以自动选择的通知.
    这里使用的通知来告知跟视图是否自动旋转, 也可以使用NSUserDefaults** 来传递这个值, 或者单例都可以.**

    到此,对于控制屏幕的旋转及方向,基本就介绍完了,如果有不足或者不对的地方,还请指正;如果你有更好的方式实现同样的效果,还请不吝赐教...

    附加

    最后介绍一个强制旋转屏幕的方法,这个方法可以在进入某个视图时,强制转成你需要的屏幕方向,用的比较多的是在一个竖屏的应用中强制转换某一个界面为横屏(例如播放视频):

    PS: 这个方法的使用有个前提, 一定是在跟视图的-(BOOL)shouldAutorotate返回值为YES的时候才有效.

    //强制旋转屏幕  
    - (void)orientationToPortrait:(UIInterfaceOrientation)orientation {  
        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];//前两个参数已被target和selector占用  
        [invocation invoke];  
          
    }  
    

    使用的时候,只需要把你需要旋转的方向传过去即可!
    有一点需要注意:从A进入B的时候, 把B强制转换成横屏,返回的时候,需要在A出现的时候再转换为原来的方向,不然会有问题;个人建议可以在BviewWillAppear调用这个方法,转换屏幕(例如转换为横屏),然后在AviewWillAppear中转换回来; 如果使用通知来告诉跟视图是否可以自动旋转, 这个方法要在通知发送之后调用.

    具体效果及设置, 可参考demo: LZInterfaceRotation

    (完)

    相关文章

      网友评论

      • 不辣先生:横屏的时候通知从侧面出来,有什么好的解决方案,老哥
      • NiceBoy:写的听明白,容我试一试
      • 经文纬武:下载了你的demo,无效啊, secondViewController 并不能变成横屏。什么原因啊
        流火绯瞳:@花下眠 文章最后有强制横屏的代码,添加进去
        经文纬武:@流火绯瞳 您好, 如何强制呢,我的需求是第二个页面强制横屏,且只支持横屏. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        //只支持竖屏
        return UIInterfaceOrientationMaskLandscapeRight;
        } 无效啊
        流火绯瞳:弹出第二个视图的时候, 没有强制转成横屏的, 需要你改变手机或者模拟器的朝向, 屏幕会跟着旋转; 如果需要强制转向的话, 在弹出的方法里或者弹出的视图里加上强制转换的代码即可
      • 484eb8f59879:你好 你最后的强制改变屏幕方向 我试了没有效果是为什么
        484eb8f59879:@流火绯瞳 我改了下,可以自由切换了,但是还是有几率没有切成功,退出app再进来屏幕就转过来了,这种可能是什么原因?
        流火绯瞳:@484eb8f59879 看看是不是不支持这个方向,还有就是根视图是不是锁定了
      • AllenZYQ:写的很赞。
      • 西叶lv:跟视图的Navigation,按照写的,完全无效!!
        西叶lv:@流火绯瞳 这种方法是不是要求,设备的屏幕锁定开关关闭???
        西叶lv:@流火绯瞳 我的导航是window的跟视图,设置了那三个方法,push出的VC要求横屏,我也在这个VC里写了这三个方法,然后无效
        流火绯瞳:应该不会, 不知道你的导航是加到哪里的, 你可以尝试另一种方法:
        UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")
      • 747fa7f32524:搜了很久Stackoverflow都没解决了我的疑惑,知道看到这篇文章,感谢~
      • liyaoyao:请问我在切换横竖屏的时候没有了过渡动画 ,楼主有没有遇到这种情况 ?
        流火绯瞳:@liyaoyao 这个没遇到,不知道你是怎么设置的
        liyaoyao:是的 ,我的横竖屏切换就直接过来了,很突兀,不是有过渡动画的时候平滑的那样切换
        流火绯瞳:@liyaoyao 过渡动画? 就是旋转么??
      • Z了个L:不行,我就把他这块都给注释了,尝试一下。
        流火绯瞳:@Z了个L 302934443
        Z了个L:我把他这段代码注释了,然后在用你的导航控制器的代码,
        发现整个项目还是可以横屏,不能特定界面横屏,大牛,
        不然我加一下你qq,或者你加一下我的,我把一个小demo,
        发给你,看看有什么问题,这个第三方毕竟也有1700多颗星星。
        麻烦大牛了, :grin:
        流火绯瞳:@Z了个L 他这段用的也是比较旧的代码了,你可以把这几段控制转屏的注释掉,然后自己加
      • Z了个L:viewController==<XMDBaseNavigationViewController: 0x12f110c00>
        viewController==<XMDBaseNavigationViewController2: 0x12f879400>
        viewController==<XMDBaseNavigationViewController1: 0x12f08b800>

        在第三方循环里面,打印了一下viewController,是我自定义的三个导航控制器,这个没有问题
      • Z了个L:把你刚刚给的加进去之后,
        主要显示3个界面,tabbar可以在3个不同的主界面切换,我就自定义了3个不同的导航控制器,分别包装一个主界面,第一种情况,什么都不写在3个导航控制器.m文件里面,那么整个项目都支持横竖屏,第二种情况,在1号导航控制器的.m文件里面写了
        #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
        - (NSUInteger)supportedInterfaceOrientations
        #else
        - (UIInterfaceOrientationMask)supportedInterfaceOrientations
        #endif
        {
        return UIInterfaceOrientationMaskAll;
        }
        - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
        return YES;
        }
        整个项目还是都可以横竖屏,
        第三种情况:我在1号里面仍然是上面的代码,但是在2号控制器.m文件添加
        #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_9_0
        - (NSUInteger)supportedInterfaceOrientations
        #else
        - (UIInterfaceOrientationMask)supportedInterfaceOrientations
        #endif
        {
        return UIInterfaceOrientationMaskPortrait;
        }
        就这个代码,然后整个项目都不能横屏了。

        我怎么感觉这个第三方有毒哦,巨坑,已经凌乱了
      • Z了个L:RDVTabBarController,你好,我的项目用的是这个第三方做底部Tabbar,然后也想指定的页面横屏或者竖屏,用你的方法,好像都失效了。所以,想和大牛探讨探讨。
        Z了个L:- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
        for (UIViewController *viewCotroller in [self viewControllers]) {
        if (![viewCotroller respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)] ||
        ![viewCotroller shouldAutorotateToInterfaceOrientation:toInterfaceOrientation]) {
        return NO;
        }
        }
        return YES;
        }

        第三方自动支持横竖屏, 这个也是其中的源代码,还请大牛指点指点。
        Z了个L:- (NSUInteger)supportedInterfaceOrientations {
        UIInterfaceOrientationMask orientationMask = UIInterfaceOrientationMaskAll;
        for (UIViewController *viewController in [self viewControllers]) {
        if (![viewController respondsToSelector:@selector(supportedInterfaceOrientations)]) {
        return UIInterfaceOrientationMaskPortrait;
        }

        UIInterfaceOrientationMask supportedOrientations = [viewController supportedInterfaceOrientations];

        if (orientationMask > supportedOrientations) {
        orientationMask = supportedOrientations;
        }
        }


        return orientationMask;
        }
        因为这个第三方起到了Tabbar的作用,所以,他的viewController装载的都是自定义的导航控制器,然后用你的导航控制器的方法,然后发现,就有问题了。这个是第三方的源码。 也不知道如何修改其源代码,达到效果
        流火绯瞳:可能需要修改他的一些代码
      • cat昵称居然被占用了:感谢,请问能转载吗,我会注明出处的
        流火绯瞳:@cat昵称居然被占用了 没事,欢迎转载,本来就是互相学习的 :smile:
      • RunningMan_Fly:setOrientation这个方法属于苹果的私有API吗
        Miles_miles:@doufutang 没有,后来这个功能没有做
        doufutang:@zhiyiLiu 请问,有没有试过发布,会不会被苹果拒绝?
        Miles_miles:@RunningMan_Fly 是,不过据说风险很低
      • 只因为趁年轻:解决了我烦恼了好久的问题,太感谢楼主的文章了. :joy:
        流火绯瞳:@只因为趁年轻 太客气了,很高兴能帮到你:smile:
      • 就让我安安静静的做一个美女子:写的真好,赞赞赞
        流火绯瞳:@就让我安安静静的做一个美女子 谢谢!:joy:

      本文标题:[iOS]代码控制APP中视图横屏/竖屏切换

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