254
社区成员




在开发旋转场景的需求的时候,由于复杂的多级配置和数目繁多的枚举类型,难免会遇到一些崩溃和无法旋转的问题,下面我们就来总结一下此类问题。
首先检查下系统屏幕旋转开关是否被锁定。系统屏幕锁定开关打开后,应用内无法自动旋转,但是可以调用上文提到的的方法进行手动旋转。
以下方法都可以设置屏幕旋转的全局权限:
Device Orientation 属性配置:“TARGETS > General > Deployment Info > Device Orientation”,图中是 xcode 默认的配置,值得注意的是 iPhone 不支持旋转到 Upside Down 方向。
// 返回需要支持的方向
// 如果我们实现了Appdelegate的这一方法,那么我们的App的全局旋转设置将以这里的为准
- (UIInterfaceOrientationMask)application:(UIApplication *)applicatio supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window {
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait;
}
以上两种方式优先级:Appdelegate方法 > Target配置,这两种方式的配置和控制器的 supportedInterfaceOrientations 方法都会影响最终视图控制器最终支持的方向。以 iOS 14 中以 present 打开控制器的方式为例,当前控制器最终支持的屏幕方向,取决于上面两种方式中的优先级最高的方式的值,与控制器 supportedInterfaceOrientations 的交集。
总结起来有以下几种情况:
这里建议如果没有全局配置的需求,就不要变更 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 实时音视频引擎,那么在旋转的场景中你就要考虑到旋转对推拉流的影响,以 RoomKit SDK 的使用场景为例,大致有以下几种情况:
ZegoVideoConfig *videoConfig = [[ZegoVideoConfig alloc] init];
// 左横屏分辨率设置如下:
videoConfig.encodeResolution = CGSizeMake(1280, 720);
[[ZegoExpressEngine sharedEngine] setVideoConfig:videoConfig];
// 调用 setAppOrientation 接口设置视频的朝向
[[ZegoExpressEngine sharedEngine] setAppOrientation:UIInterfaceOrientationLandscapeLeft];
// 根据当前方向设置分辨率
ZegoVideoConfig *videoConfig = [ZegoVideoConfig defaultConfig];
if (isCurPortrait) {
videoConfig.captureResolution = CGSizeMake(720, 1280);
} else {
videoConfig.captureResolution = CGSizeMake(1280, 720);
}
// 调用 setAppOrientation 接口设置视频的朝向
[[ZegoExpressEngine sharedEngine] setAppOrientation:[UIApplication sharedApplication].statusBarOrientation];
在前面的讲述中,我们也认识了一些与屏幕旋转相关的枚举值。乍一看这块内容确实会感觉多得让人眼花缭乱,但是我们看清楚他们名称中的关键词如: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