iOS 屏幕旋转的实践解析——下篇

ZEGO即构科技
企业官方账号
2021-09-13 18:39:38

三、相关问题

在开发旋转场景的需求的时候,由于复杂的多级配置和数目繁多的枚举类型,难免会遇到一些崩溃和无法旋转的问题,下面我们就来总结一下此类问题。

问题一:无法自动旋转

首先检查下系统屏幕旋转开关是否被锁定。系统屏幕锁定开关打开后,应用内无法自动旋转,但是可以调用上文提到的的方法进行手动旋转。

图片

 

问题二:多级屏幕旋转控制设置错误

以下方法都可以设置屏幕旋转的全局权限:

  • Device Orientation 属性配置:“TARGETS > General > Deployment Info > Device Orientation”,图中是 xcode 默认的配置,值得注意的是 iPhone 不支持旋转到 Upside Down 方向。

​​​​

图片

 

  • Appdelegate的 supportedInterfaceOrientationsForWindow 方法:
// 返回需要支持的方向
// 如果我们实现了Appdelegate的这一方法,那么我们的App的全局旋转设置将以这里的为准
- (UIInterfaceOrientationMask)application:(UIApplication *)applicatio supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
    return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait;
}

 

以上两种方式优先级:Appdelegate方法 > Target配置,这两种方式的配置和控制器的 supportedInterfaceOrientations 方法都会影响最终视图控制器最终支持的方向。以 iOS 14 中以 present 打开控制器的方式为例,当前控制器最终支持的屏幕方向,取决于上面两种方式中的优先级最高的方式的值,与控制器 supportedInterfaceOrientations 的交集。

 

总结起来有以下几种情况:

 

  • 如果交集为空,且在控制器的 shouldAutorotate 方法中返回为 YES,则会发生UIApplicationInvalidInterfaceOrientation 的崩溃。
  • 如果交集为空,且在控制器的 shouldAutorotate 方法中返回为 NO,控制器的supportedInterfaceOrientations 方法与 preferredInterfaceOrientationForPresentation 方法返回值不冲突(前者返回值包含有后者返回值),则显示为控制器配置的方向。
  • 如果交集为空,且在控制器的 shouldAutorotate 方法中返回为 NO,控制器的supportedInterfaceOrientations 方法与 preferredInterfaceOrientationForPresentation 方法返回值冲突(前者返回值未包含有后者返回值),则会发生 UIApplicationInvalidInterfaceOrientation 的崩溃。
  • 如果交集不为空,控制器的 supportedInterfaceOrientations 方法与 preferredInterfaceOrientationForPresentation 方法返回值冲突,则会发生 UIApplicationInvalidInterfaceOrientation 的崩溃。
  • 如果交集不为空,控制器的 supportedInterfaceOrientations 方法与 preferredInterfaceOrientationForPresentation 方法返回值不冲突,当前控制器则根据 shouldAutorotate 返回值决定是否在交集的方向内自动旋转。

这里建议如果没有全局配置的需求,就不要变更 Target 属性配置或实现 Appdelegate 方法,只需在要实现旋转效果的 ViewController 中按前面所说的方式去实现代码。

问题三:横屏时打开系统锁定屏幕开关,视图被强制恢复到竖屏

由于 iOS 闭源,苹果为什么会这样操作当然我们也无从得知,但是我们可以通过一些手段来规避这个问题。好在产生这样的旋转时,系统也会触发和普通旋转时一样的方法调用。

以 iPhone X 为例,当下拉打开控制页面时,我们会收到 UIApplicationWillResignActiveNotification 的系统通知,收起控制页面后会收到 UIApplicationDidBecomeActiveNotification 通知,通过这两个通知来记录一下状态,在 shouldAutorotate 通过判断是否是 Active 状态 返回 YES/NO。

- (void)setupNotification {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationWillResignActive:)
                                                 name:UIApplicationWillResignActiveNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(applicationDidBecomeActive:)
                                                 name:UIApplicationDidBecomeActiveNotification object:nil];
}


- (BOOL)shouldAutorotate {
    if (!self.isApplicationActive) {
            return NO;
        } else {
            return YES;
        }
    }
}

问题四:屏幕旋转与 ZegoExpressEngine 的适配

有很多小伙伴已经接入了我们的 ZegoExpressEngine 实时音视频引擎,那么在旋转的场景中你就要考虑到旋转对推拉流的影响,以 RoomKit SDK 的使用场景为例,大致有以下几种情况:

  • 当前页面固定一个方向显示,只需要设置与当前方向符合的视频分辨率(引擎默认值为 “360 × 640”,根据自己需求确定),再调用引擎的 setAppOrientation 接口设置当前方向,以下代码以左横屏方向为例:
ZegoVideoConfig *videoConfig = [[ZegoVideoConfig alloc] init];
// 左横屏分辨率设置如下:
videoConfig.encodeResolution = CGSizeMake(1280, 720);
[[ZegoExpressEngine sharedEngine] setVideoConfig:videoConfig];
// 调用 setAppOrientation 接口设置视频的朝向
[[ZegoExpressEngine sharedEngine] setAppOrientation:UIInterfaceOrientationLandscapeLeft];
  • 当前页面有旋转的场景,这时就需要在旋转完成后去更新 ZegoExpressEngine 引擎的方向和视频分辨率,注意这里的当前方向取的是当前状态栏的方向。
// 根据当前方向设置分辨率
ZegoVideoConfig *videoConfig = [ZegoVideoConfig defaultConfig];
if (isCurPortrait) {
    videoConfig.captureResolution = CGSizeMake(720, 1280);
} else {
    videoConfig.captureResolution = CGSizeMake(1280, 720);
}
// 调用 setAppOrientation 接口设置视频的朝向
[[ZegoExpressEngine sharedEngine] setAppOrientation:[UIApplication sharedApplication].statusBarOrientation];
  • 上面的 ZegoExpressEngine 音视频引擎屏幕旋转后的适配逻辑,处理时机都在视图控制器旋转完成后,也就是 viewWillTransitionToSize 方法的 completion block 里面,这时拿到的 [UIApplication sharedApplication].statusBarOrientation 方向与当前控制器方向符合。更多 ZegoExpressEngine 音视频引擎屏幕旋转问题可以参考:https://doc-zh.zego.im/article/4823?w=

四、相关枚举值

在前面的讲述中,我们也认识了一些与屏幕旋转相关的枚举值。乍一看这块内容确实会感觉多得让人眼花缭乱,但是我们看清楚他们名称中的关键词如:Device、Interface,并在各个枚举类型用到的地方去理解它的意思,也是能理清这里面的逻辑的。

1、 设备方向:UIDeviceOrientation

UIDeviceOrientation 是以 home 键的位置作为参照,受传感器影响,和当前屏幕显示的方向无关,所以只能取值不能设值。

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} API_UNAVAILABLE(tvos);

 

前面讲述的屏幕旋转方法中不会直接用到这个枚举,但是如果你有监听设备当前方向的需求时,它就变得有用了。可以通过 [UIDevice currentDevice].orientation 获取当前设备的方向,若要监听设备的方向变化,可以用以下代码实现:

[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
 [[NSNotificationCenter defaultCenter] addObserver:observer
                                          selector:@selector(onDeviceOrientationChange:)
                                              name:UIDeviceOrientationDidChangeNotification
                                            object:nil];

2、 页面方向:UIInterfaceOrientation

UIInterfaceOrientation 是当前视图控制器的方向,区别于设备方向,它是屏幕正在显示的方向,preferredInterfaceOrientationForPresentation 方法的返回值就是这个枚举类型。

/// 优先的屏幕方向
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationPortrait;
}

注意 UIInterfaceOrientationLandscapeLeft 与 UIDeviceOrientationLandscapeRight 是对应的,这两个枚举类型左右相反。

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
} API_UNAVAILABLE(tvos);

3、 页面方向:UIInterfaceOrientationMask

观察 UIInterfaceOrientationMask 枚举的值,我们就会发现这是一种为了支持多种UIInterfaceOrientation 而定义的类型,它用来作为 supportedInterfaceOrientations 方法的返回值,比如我们在该方法中返回 UIInterfaceOrientationMaskAll 就可以支持所有方向了。

/// 当前 VC支持的屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return UIInterfaceOrientationMaskAll;
}
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),
} API_UNAVAILABLE(tvos);

五、结尾

ZEGO RoomKit SDK 目前已经支持屏幕旋转场景,并且在 2.0.0 版本中以 JSON 配置的形式,支持更灵活更便捷的实现自定义的屏幕旋转场景。

在视频直播类的 APP 中屏幕旋转往往是绕不开的一环,梳理清楚以上三个枚举的含义,以及旋转方法的调用时机,并在恰当的时间去刷新旋转后的布局,iOS旋转适配就不再困难。

以上就是关于在 iOS 上实现屏幕旋转的技术解读,也欢迎大家使用 RoomKit SDK 体验 demo,点击链接,即可进行体验:https://doc-zh.zego.im/scene-plan/23

 

阅读提醒:iOS 屏幕旋转的实践解析——上篇    https://bbs.csdn.net/topics/600546177

...全文
5762 回复 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

254

社区成员

发帖
与我相关
我的任务
社区描述
音视频技术干货的分享聚集!
音视频学习 企业社区
社区管理员
  • ZEGO即构
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

这里是音视频开发者小伙伴的聚集大本营!大家可在此集中讨论RTC开发相关内容、工作中遇到的问题、资源求助等。即构期待与大家共同打造一个开放、包容的RTC构建交流社区~

试试用AI创作助手写篇文章吧