关于ios:iOS-屏幕旋转的实践解析

46次阅读

共计 9809 个字符,预计需要花费 25 分钟才能阅读完成。

摘要:如何更灵便便捷的实现自定义屏幕旋转场景,本文带你揭秘!

 
文|即构 iOS 利用开发团队

屏幕旋转是在视频直播类 APP 中常见的场景,在即构科技之前公布的 Roomkit SDK 中也有屏幕追随手机主动旋转的场景。

在 Roomkit SDK 本身开发和客户接入的过程中咱们也会发现,实现屏幕旋转的需要往往没有那么顺利,常常会呈现无奈旋转、旋转后布局适配等问题。

本篇文章依据咱们以往的开发教训整顿了屏幕旋转实现的相干实际办法,解析在实现过程中遇到的常见问题。

一、疾速实现旋转

iOS 屏幕旋转的实现波及到一堆枚举值和回调办法,对于没有做过旋转相干需要的开发来说,可能一上来就晕了,所以咱们先入手,让屏幕转起来吧。

实现旋转的形式次要有两种,追随手机感应旋转和手动旋转,接下来对这两种形式进行逐个介绍。

形式一:追随手机感应器旋转

要实现主动追随手机旋转,首先要让以后的视图控制器实现以下三个办法:

/// 是否主动旋转
- (BOOL)shouldAutorotate {return YES;}

/// 以后 VC 反对的屏幕方向
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;}

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

这种办法须要留神以下几点:

shouldAutorotate 返回 YES 示意追随零碎旋转,然而受 supportedInterfaceOrientations 办法的返回值影响,只反对追随手机传感器旋转到反对的方向。

preferredInterfaceOrientationForPresentation 须要返回 supportedInterfaceOrientations 中反对的方向,不然会产生 ‘UIApplicationInvalidInterfaceOrientation’ 解体。

形式二:手动旋转

这种形式在很多视频软件中都很常见,点击按钮后旋转至横屏。

这时须要在 shouldAutorotate 中返回 yes,而后再在此办法中 UIInterfaceOrientation 传入你须要旋转到的方向。留神这是公有办法,是否应用请自行斟酌。

- (void)changeVCToOrientation:(UIInterfaceOrientation)orientation {if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {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];
        [invocation invoke];
    }
}

场景利用

  • 主动旋转

如果你的 iPhone 没有关闭系统屏幕旋转,你就能发现零碎相册 APP 的页面是能够跟着手机转动方向旋转的。

如果你想实现和它一样的成果,只须要依照后面形式一(追随手机感应器旋转)去配置你的视图控制器的办法,之后控制器就能够在 supportedInterfaceOrientations 返回的方向内实现自在旋转了。

  • 只能手动旋转

这种场景比拟少见,在视频直播类 APP 中常见的场景是主动和手动旋转相结合的形式。

如果你要实现只能通过像点击按钮去旋转的形式,首先须要在 supportedInterfaceOrientations 办法中返回你须要反对的方向,这里重点是 shouldAutorotate 办法的返回值。

下面形式二中(手动旋转)阐明了手动旋转须要 shouldAutorotate 返回 YES,然而这也会让控制器反对主动旋转,不合乎这个需要,所以咱们按以下办法解决:

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

属性 isRotationNeeded 作为是否须要旋转的标记,isRotationNeeded 默认为 NO,此时就算你旋转设施,回调 shouldAutorotate 办法时也不会返回 YES,所以屏幕也不会主动旋转。

剩下的只须要你在点击旋转的按钮后将 isRotationNeeded 置为 YES 并调用手动旋转的办法,这样解决后只能手动旋转的成果就实现了。

二、旋转后的 UI 布局更新

通常状况下,利用旋转到横竖屏后,因为不同的宽高比会有不同 UI,所以在屏幕旋转的场景中咱们又须要解决旋转后 UI 适配的问题。

手机旋转时,失常状况下若 shouldAutorotate 返回 YES , 当视图控制器须要旋转就会触发 viewWillTransitionToSize 办法,这样咱们就找到了去更新横竖屏 UI 的机会了,也就是在 completion block 里去实现旋转后的适配逻辑。

/*
This method is called when the view controller's view's size is
changed by its parent (i.e. for the root view controller when its window rotates or is resized).

If you override this method, you should either call super to
propagate the change to children or manually forward the 
change to children.
 */
- (void)viewWillTransitionToSize:(CGSize)size
       withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
    
    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        // 横屏:size.width > size.height
        // 竖屏: size.width < size.height
        NSLog(@"旋转实现,更新布局");
    
    }];
}

三、相干问题

在开发旋转场景的需要的时候,因为简单的多级配置和数目繁多的枚举类型,难免会遇到一些解体和无奈旋转的问题,上面咱们就来总结一下此类问题。

问题一:无奈主动旋转

首先查看下零碎屏幕旋转开关是否被锁定。零碎屏幕锁定开关关上后,利用内无奈主动旋转,然而能够调用上文提到的的办法进行手动旋转。

问题二:多级屏幕旋转管制设置谬误

以下办法都能够设置屏幕旋转的全局权限:

  • 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

正文完
 0