• iOS 16适配屏幕旋转强制转屏切换大总结


    问题原因:

    苹果又给我们挖坑了,iOS 16屏幕旋转报错:[Orientation] BUG IN CLIENT OF UIKIT: Setting UIDevice.orientation is not supported. Please use UIWindowScene.requestGeometryUpdate(_:)

    坑:听说xcode 14 和 xcode 13编译出的安装包效果不一,经测试确实如此!还是要打包测试完毕以后再上线哦!

    解决办法:

    坑1、

    经过实验,以前的方法直接给UIDevice  setOrientation: 的方式还是生效的,只不过需要适配一下。

    首先我们应该注意到iOS 16新增加了一个方法:setNeedsUpdateOfSupportedInterfaceOrientations

    1. /// Notifies the view controller that a change occurred that affects supported interface orientations or the preferred interface orientation for presentation.
    2. /// By default, this will animate any changes to orientation. To perform a non-animated update, call within `[UIView performWithoutAnimation:]`.
    3. - (void)setNeedsUpdateOfSupportedInterfaceOrientations API_AVAILABLE(ios(16.0));

    这和更新状态栏的方法有点像,简单点说就是你想转屏可以,需要通知UIViewController 你已经准备好要转屏的方向了,然后再调用转屏方法即可(转屏方法后面会讲到)。

    坑2、

    调用完转屏方法以后,view需要重新更新frame,这时候你会发现获取到的屏幕宽高并不是你要转屏以后的结果。难道是在iOS 16中转屏不及时更新UIScreen的size了? 可能是吧!这里我们就需要自己判断一下到底需要什么样的宽度和高度啦!

    坑3、

    据我实验 - (BOOL)shouldAutorotate{} 在iOS 16中不再起效果!不管返回YES还是NO都能转屏!!!反而是需要控制- (UIInterfaceOrientationMask)supportedInterfaceOrientations有效果,神奇不!!

    坑4、

    据我实验在iOS 16中转屏的时候,直接获取设备方向:

    UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;

    将返回UIDeviceOrientationUnknown,是不是神坑!!!!!

    转屏方法总结:

    iOS 16以前的写法:如果你还在用Xcode13,且配合使用setNeedsUpdateOfSupportedInterfaceOrientations,此方案编译的包在iOS 16上依然有效!

    1. if ([[UIDevice currentDevice] respondsToSelector:@selector(setOrientation:)]) {
    2. SEL selector = NSSelectorFromString(@"setOrientation:");
    3. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:selector]];
    4. [invocation setSelector:selector];
    5. [invocation setTarget:[UIDevice currentDevice]];
    6. int val = UIDeviceOrientationPortrait;
    7. [invocation setArgument:&val atIndex:2];
    8. [invocation invoke];
    9. }
    10. [UIViewController attemptRotationToDeviceOrientation];

    当然,据实验所知,iOS 16以后也能用,只不过会有日志警告。如果你用Xcode 14 ,此方案在iOS 16上就不好用了!

    iOS 16的写法:建议升级Xcode 14

    1. if (@available(iOS 16.0, *)) {
    2. // setNeedsUpdateOfSupportedInterfaceOrientations 方法是 UIViewController 的方法
    3. [self setNeedsUpdateOfSupportedInterfaceOrientations];
    4. NSArray *array = [[[UIApplication sharedApplication] connectedScenes] allObjects];
    5. UIWindowScene *scene = [array firstObject];
    6. // 屏幕方向
    7. UIInterfaceOrientationMask orientation = isLaunchScreen ? UIInterfaceOrientationMaskLandscape: UIInterfaceOrientationMaskPortrait;
    8. UIWindowSceneGeometryPreferencesIOS *geometryPreferencesIOS = [[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:orientation];
    9. // 开始切换
    10. [scene requestGeometryUpdateWithPreferences:geometryPreferencesIOS errorHandler:^(NSError * _Nonnull error) {
    11. NSLog(@"错误:%@", error);
    12. }];
    13. }

    当然如果你没有升级到Xcode 14,还可以这样写:(据我实验不好用,只能横屏,不能再转到竖屏,不推荐此方案!)

    1. if (@available(iOS 16.0, *)) {
    2. void (^errorHandler)(NSError *error) = ^(NSError *error) {
    3. NSLog(@"错误:%@", error);
    4. };
    5. #pragma clang diagnostic push
    6. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    7. SEL supportedInterfaceSelector = NSSelectorFromString(@"setNeedsUpdateOfSupportedInterfaceOrientations");
    8. [self performSelector:supportedInterfaceSelector];
    9. NSArray *array = [[UIApplication sharedApplication].connectedScenes allObjects];
    10. UIWindowScene *scene = (UIWindowScene *)[array firstObject];
    11. Class UIWindowSceneGeometryPreferencesIOS = NSClassFromString(@"UIWindowSceneGeometryPreferencesIOS");
    12. if (UIWindowSceneGeometryPreferencesIOS) {
    13. SEL initWithInterfaceOrientationsSelector = NSSelectorFromString(@"initWithInterfaceOrientations:");
    14. UIInterfaceOrientationMask orientation = UIInterfaceOrientationMaskPortrait;
    15. id geometryPreferences = [[UIWindowSceneGeometryPreferencesIOS alloc] performSelector:initWithInterfaceOrientationsSelector withObject:@(orientation)];
    16. if (geometryPreferences) {
    17. SEL requestGeometryUpdateWithPreferencesSelector = NSSelectorFromString(@"requestGeometryUpdateWithPreferences:errorHandler:");
    18. if ([scene respondsToSelector:requestGeometryUpdateWithPreferencesSelector]) {
    19. [scene performSelector:requestGeometryUpdateWithPreferencesSelector withObject:geometryPreferences withObject:errorHandler];
    20. }
    21. }
    22. }
    23. #pragma clang diagnostic pop
    24. }

    另外一个需求:如果你的页面不需要自动转屏,只需要点击按钮触发转屏。你还可以这样写:

    横屏按钮点击事件:

    1. if (@available(iOS 16, *)) {
    2. _landscape = YES;
    3. [self setNeedsUpdateOfSupportedInterfaceOrientations];
    4. }

    竖屏按钮点击事件:

    1. if (@available(iOS 16, *)) {
    2. _landscape = NO;
    3. [self setNeedsUpdateOfSupportedInterfaceOrientations];
    4. }

    再加一个方法:

    1. - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    2. if (_landscape)
    3. {
    4. //横屏
    5. return UIInterfaceOrientationMaskLandscape;
    6. }else
    7. {
    8. //竖屏
    9. return UIInterfaceOrientationMaskPortrait;
    10. }
    11. }

    极简单的代码即可完成你想要的效果。

    注意事项:

    如果你的[self setNeedsUpdateOfSupportedInterfaceOrientations] 没起任何作用,你可以需要在主线程main queue中调用它!

    总结完毕!有疑问欢迎下方留言讨论!

    HXRotationTool更新:iOS 屏幕旋转工具类,兼容iOS 16,兼容Xcode 13和Xcode 14。

    GitHub - TheLittleBoy/HXRotationTool: iOS 屏幕旋转工具类,兼容iOS 16,兼容Xcode 13和Xcode 14。

    如图:

    喜欢的小伙伴就帮忙点个收藏吧~ 

  • 相关阅读:
    【Opencv】OpenCV使用CMake和MinGW的编译安装出错解决
    【MySQL知识体系】第1章 初识 MySQL
    文件服务之FTP
    Jenkins kubernetes(k8s)滚动发布实战
    【MATLAB】求解含有三角函数的方程
    Leetcode.321 拼接最大数
    Glide源码解析四(解码和转码)
    Jpg格式如何转成gif格式动图?简单一招搞定gif制作
    「PAT甲级真题解析」Advanced Level 1008 Elevator
    WPF的简介以及创建
  • 原文地址:https://blog.csdn.net/xuexixiaoshizhe/article/details/126955521