iOS中sdk横竖屏切换适配方案

1,616 阅读4分钟

在 iOS 开发中,横竖屏切换是一个经常遇到的场景,比如页面包含视频播放器全屏时需要切换为横屏、某些页面设计为横屏显示内容、某些页面支持横竖屏切换等等。如果是APP开发中遇到这些场景,开发者知道哪些页面支持横屏、哪些页面支持横竖屏,处理起来就相对比较简单。而如果你是提供sdk给其他APP使用,像我们 finclip sdk这样,提供一个小程序容器给其他APP使用,小程序页面可以设置支持不同的方向,还会包含视频播放器全屏时需要切换横竖屏,但APP有可能只支持竖屏,这时sdk该如何做适配呢?

页面方向如何确定的分析

首先我们分析下iOS中确定页面方向的流程 iOS中影响页面方向的有以下几个因素:

  1. APP工程配置支持的页面方向
  2. APPDelegate中application:supportedInterfaceOrientationsForWindow:支持的页面方向
  3. 当前视图控制器supportedInterfaceOrientations支持的页面方向
  4. 当前视图控制器shouldAutorotate,是否支持旋转 为了提供良好的用户体验,我们需要正确处理不同的屏幕方向。本文将详细介绍如何在 iOS 应用中进行横竖屏方向适配,包括工程方向设置、AppDelegate 的方向代理、导航控制器、模态视图的适配,以及如何在某个页面包含视频时强制横屏。

页面方向确定的具体流程如下:

  1. 页面要显示首先会触发APPDelegate中application:supportedInterfaceOrientationsForWindow:方法, 如果APP没有实现该方法,会返回APP工程配置的方向。APP也可以实现该方法,通过参数window,获取将要显示的视图控制器,返回需要支持的方向。
  2. 检查当前显示的视图控制器的 supportedInterfaceOrientations 方法 当前页面可以重写该方法,返回支持的方向A,这里会与APPDelegate中application:supportedInterfaceOrientationsForWindow:方法返回的方向B做比较,如果B不支持A方向,则会导致崩溃。
  3. 调用视图控制器的 shouldAutorotate 方法,以判断视图控制器是否允许旋转,如果允许旋转(不重写的话默认返回的是YES),系统将改变页面的方向到指定方向。

sdk横竖屏方向的适配

通过上面的分析,我们知道如果APP只支持竖屏,sdk的页面想支持横屏,就得在页面supportedInterfaceOrientations返回横屏方向,这时会与APPDelegate中application:supportedInterfaceOrientationsForWindow:返回的竖屏冲突,会导致崩溃。如果想避免崩溃,又想当前页面支持横屏,可以按照下面的适配方法:

首先需要宿主APP在APPDelegate实现代理方法,对指定页面控制器返回横屏的支持,最好是sdk的视图控制器基类,这样就不用列举sdk中特定视图控制器类了,也避免sdk暴露不必要的头文件。

{
    //获取当前视图控制
    UIViewController *topVC = [application fat_topViewController];
    //sdk的视图控制器基类
    Class class = NSClassFromString(@"FATUIViewController");
    if ([topVC isKindOfClass:class]) {
       return UIInterfaceOrientationMaskAllButUpsideDown;
    }
    //否则返回APP支持的页面方向
    
}

如果需要改变页面方向,比如video组件切换全屏,页面方向切换为横屏,iOS16系统以上的处理会不一样

iOS16以上可以按下面的方式改变页面方向:

if (@available(iOS 16.0, *)) {
    //必须加上respondsToSelector判断,否则部分机型会崩溃
    if (self.navigationController && [self.navigationController respondsToSelector:@selector(setNeedsUpdateOfSupportedInterfaceOrientations)]) {
        [self.navigationController setNeedsUpdateOfSupportedInterfaceOrientations];
    } else {
        if ([self respondsToSelector:@selector(setNeedsUpdateOfSupportedInterfaceOrientations)]) {
            [self setNeedsUpdateOfSupportedInterfaceOrientations];
        }
    }
    
    // 目标方向
    UIInterfaceOrientationMask targetOrientation;
    if (deviceOrientation == UIDeviceOrientationLandscapeLeft) {
        targetOrientation = UIInterfaceOrientationMaskLandscapeRight;
    } else if (deviceOrientation == UIDeviceOrientationLandscapeRight) {
        targetOrientation = UIInterfaceOrientationMaskLandscapeLeft;
    }
    else {
        targetOrientation = UIInterfaceOrientationMaskPortrait;
    }
    
    NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
    UIWindowScene *ws = (UIWindowScene *)array[0];
    [UIViewController attemptRotationToDeviceOrientation];
    UIWindowSceneGeometryPreferencesIOS *geometryPreferences = [[UIWindowSceneGeometryPreferencesIOS alloc] init];
    eometryPreferences.interfaceOrientations = targetOrientation;
    [ws requestGeometryUpdateWithPreferences:geometryPreferences                                errorHandler:^(NSError * _Nonnull error) {        if (error) {                    }    }];
}

iOS16以下系统能需要强制改变设备方向:

[[UIDevice currentDevice] setValue:@(deviceOrientation) forKey:@"orientation"];
[UIViewController attemptRotationToDeviceOrientation];

上面的方案如果APP端没有实现application:supportedInterfaceOrientationsForWindow:方法,这时打开sdk中的页面可能会导致崩溃,造成不好的用户体验。为了避免这种情况下导致崩,可以在页面的supportedInterfaceOrientations加上判断代码来防止崩溃。

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    //先读取appdelegate支持的方向
    UIInterfaceOrientationMask appOrientations;
    UIApplication *application = [UIApplication sharedApplication];
    if ([application.delegate respondsToSelector:@selector(application:supportedInterfaceOrientationsForWindow:)]) {
        appOrientations = [application.delegate application:application supportedInterfaceOrientationsForWindow:self.view.window];
    } else {
       appOrientations = [[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.view.window];
    }
    //读取页面支持的方向
    UIInterfaceOrientationMask pageOrientations = [self fat_pageSupportedInterfaceOrientations];
    //页面方向和APP支持的方向取交集,如果没有交集,则返回APP支持的方向,来避免崩溃
    return [self fat_allowOrientation:pageOrientations withAppOrientation:appOrientations];
    
}

以上就是在 finclip sdk中页面方向适配中的一点心得,有兴趣可以去体验下。如果有更好的适配方案,可以在评论区告诉我。