美文网首页iOS开发大全
iOS屏幕旋转的奥秘

iOS屏幕旋转的奥秘

作者: 杨志聪 | 来源:发表于2020-04-16 21:31 被阅读0次

    本文从一个页面支持的方向、屏幕的旋转、size class这三方面简述iOS屏幕旋转的奥秘。

    一个页面支持的方向

    一个页面可以支持什么方向,首先得看看这个app支持什么方向。app支持什么方向是通过全局配置来实现的。

    全局配置

    有两个地方可以对app支持的方向进行全局配置:Xcode项目配置和AppDelegate。其中AppDelegate的优先级比Xcode项目配置要高,也就是说如果你两个地方都做了配置,那么会优先采用AppDelegate的配置:

    Xcode项目配置 AppDelegate

    其中AppDelegate中是通过UIApplicationDelegate协议中的application: supportedInterfaceOrientationsForWindow:方法指定不同window支持的方向。

    这两个地方根据需要任选其一就好。配置完毕后,app能支持的方向就定下来了。

    页面配置

    上面通过全局配置把app支持的方向定下来了。但不是所有页面都需要支持app支持的所有方向。例如app支持横屏和竖屏,但是某个页面只需要支持竖屏。

    页面支持的方向,是通过重写UIViewControllershouldAutorotate方法和supportedInterfaceOrientations方法确定的。

    shouldAutorotate方法确定该页面能不能旋转:

    - (BOOL)shouldAutorotate {
        return YES;
    }
    

    supportedInterfaceOrientations方法确定该页面支持的方向:

    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        
        return UIInterfaceOrientationMaskPortrait;
    }
    

    注意如果supportedInterfaceOrientations方法返回全局配置中不包括的方向会导致app闪退。

    实际开发中很多页面是嵌套在UINavigationController或者UITabBarController中的,对于这种嵌套的情况,页面能支持的方向是由嵌套层级中最底层的页面控制器确定的。比如:UITabBarController -> UINavigationController -> UIViewController这种最经典的嵌套,页面的方向是由最底层的UITabBarController确定的。但是我们希望由顶层的页面自己决定自己支持的方向,为了实现这种效果,可以使用UINavigationControllerUITabBarController的子类,并在子类中覆盖它们的shouldAutorotate方法和supportedInterfaceOrientations方法:

    UINavigationController子类中:

    - (BOOL)shouldAutorotate {
        
        return self.topViewController.shouldAutorotate;
    }
    
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        
        return self.topViewController.supportedInterfaceOrientations;
    }
    

    UITabBarController子类中:

    - (BOOL)shouldAutorotate {
        
        return self.selectedViewController.shouldAutorotate;
    }
    
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
        
        return self.selectedViewController.supportedInterfaceOrientations;
    }
    

    屏幕的旋转

    苹果希望用户可以在任意方向上使用他的设备,所以屏幕是跟随设备方向旋转的,苹果官方并没有提供接口让我们设置当前屏幕的方向。但实际开发中很多场景都需要强制旋转屏幕,例如说在竖屏下有一个全屏按钮,点击全屏按钮后旋转到横屏。目前可以通过以下代码实现强制旋转屏幕:

    UIDevice *device = [UIDevice currentDevice];
    if ([device respondsToSelector:@selector(setOrientation:)]) {
        [device setValue:@(UIDeviceOrientationLandscapeLeft) forKey:@"orientation"];
    }
    

    需要注意的是在iPad上,支持分屏功能(官方称为多任务处理功能)的app调用上面的代码是无效的。新创建的项目默认支持iPad的分屏功能。

    取消iPad分屏功能有两种方法:

    方法一:不要让app支持所有的旋转方向。分屏功能要求app能支持所有方向;

    方法二:在项目配置中勾选“Requires full screen”。表示app只能在全屏下运行。

    Requires full screen

    相反如果要app支持分屏功能,需要满足两个条件:第一要让app支持所有旋转方向;第二取消勾选“Requires full screen”。

    size class

    页面支持旋转后,有时候我们需要让横屏和竖屏有不一样的UI布局。为了实现上述效果,很自然的做法就是监听当前屏幕的方向,然后根据不同的方向更新布局。这样做不好的地方在于把我们的布局思维限定在横屏和竖屏两种状态。随着苹果产品线的丰富,从苹果电视到苹果手表,屏幕的尺寸也变得非常复杂。UI布局的目的,其实是就是充分利用屏幕空间,用恰当的方式把内容展示给用户,而不是拘泥于横屏还是竖屏。

    为了达到这种目的,苹果在iOS8推出了size class。size class把屏幕分为四类:

    1. 宽度受限,高度受限;

    2. 宽度受限,高度标准;

    3. 宽度标准,高度受限;

    4. 宽度标准,高度标准。

    例如iPhone 11在竖屏的情况下,属于“宽度受限,高度标准”,横屏的情况下是“宽度标准,高度受限”。而iPad无论横屏还是竖屏,都是“宽度标准,高度标准”。

    空间标准,表示我们可以大胆发挥UI布局,尽量显示更多内容给用户;空间受限,表示我们要小心翼翼使用不多的空间。你不需要关心具体的空间有多少(苹果也没有告诉我们多大的空间为标准,多大的空间为受限)。

    那么如何获取到当前页面的size class呢?iOS 8开始,UIViewController实现UITraitEnvironment协议:

    @protocol UITraitEnvironment <NSObject>
    @property (nonatomic, readonly) UITraitCollection *traitCollection API_AVAILABLE(ios(8.0));
    
    /*! To be overridden as needed to provide custom behavior when the environment's traits change. */
    - (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection API_AVAILABLE(ios(8.0));
    @end
    

    页面可以通过self.traitCollection.horizontalSizeClassself.traitCollection.verticalSizeClass获取其当前的size class,通过重写traitCollectionDidChange:方法监听size class的改变,然后对UI布局进行更新。

    其实在代码中使用size class不是很方便。storyboard和xib完美地支持size class,所以如果要使用size class,建议使用storyboard和xib。关于如何在storyboard和xib中使用size class,本文就不介绍了。

    所以我们不要再纠结横屏或竖屏了,更高层次的思想应该是:在这个size class中,我采用什么样的布局更能充分利用空间

    size class的小问题

    iPad在横屏和竖屏状态下,size class都是“宽度标准,高度标准”,如果我实在是希望横屏和竖屏下采用不一样的布局,size class就失去作用了。在这种情况下,可以通过重写UIViewController的viewWillTransitionToSize: withTransitionCoordinator:方法来监听屏幕尺寸的改变,实现对不同的尺寸使用不同的布局:

    - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
        
        // 宽度大于高度,判断为横屏状态
        if (size.width > size.height) {
            // 横屏的布局...
        }
        // 竖屏状态
        else {
            // 竖屏的布局...
        }
    }
    

    相关文章

      网友评论

        本文标题:iOS屏幕旋转的奥秘

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