在 iOS 开发中,横竖屏切换是一个经常遇到的场景,比如页面包含视频播放器全屏时需要切换为横屏、某些页面设计为横屏显示内容、某些页面支持横竖屏切换等等。如果是APP开发中遇到这些场景,开发者知道哪些页面支持横屏、哪些页面支持横竖屏,处理起来就相对比较简单。而如果你是提供sdk给其他APP使用,像我们 finclip sdk这样,提供一个小程序容器给其他APP使用,小程序页面可以设置支持不同的方向,还会包含视频播放器全屏时需要切换横竖屏,但APP有可能只支持竖屏,这时sdk该如何做适配呢?
页面方向如何确定的分析
首先我们分析下iOS中确定页面方向的流程 iOS中影响页面方向的有以下几个因素:
- APP工程配置支持的页面方向
- APPDelegate中application:supportedInterfaceOrientationsForWindow:支持的页面方向
- 当前视图控制器supportedInterfaceOrientations支持的页面方向
- 当前视图控制器shouldAutorotate,是否支持旋转 为了提供良好的用户体验,我们需要正确处理不同的屏幕方向。本文将详细介绍如何在 iOS 应用中进行横竖屏方向适配,包括工程方向设置、AppDelegate 的方向代理、导航控制器、模态视图的适配,以及如何在某个页面包含视频时强制横屏。
页面方向确定的具体流程如下:
- 页面要显示首先会触发APPDelegate中application:supportedInterfaceOrientationsForWindow:方法, 如果APP没有实现该方法,会返回APP工程配置的方向。APP也可以实现该方法,通过参数window,获取将要显示的视图控制器,返回需要支持的方向。
- 检查当前显示的视图控制器的 supportedInterfaceOrientations 方法 当前页面可以重写该方法,返回支持的方向A,这里会与APPDelegate中application:supportedInterfaceOrientationsForWindow:方法返回的方向B做比较,如果B不支持A方向,则会导致崩溃。
- 调用视图控制器的 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中页面方向适配中的一点心得,有兴趣可以去体验下。如果有更好的适配方案,可以在评论区告诉我。