iOS_适配 iOS16 转屏

iOS_适配 iOS16 转屏

  • 问题1:iOS 16 屏幕旋转报错:[Orientation] BUG IN CLIENT OF UIKIT: Setting UIDevice.orientation is not supported. Please use UIWindowScene.requestGeometryUpdate(_:)
    解决:iOS16需要使用新的转屏方式,见下文代码

  • 问题2: Xcode13Xcode14 编译出的安装包效果不一致
    解决:需要区分编译环境,写两套代码

  • 问题3:- (BOOL)shouldAutorotate{} 在iOS 16 已经不起作用了,无论返回 yes or no 都能转屏。
    解决:iOS16 新增:setNeedsUpdateOfSupportedInterfaceOrientations方法,用于通知 UIViewController 支持的屏幕方向有更新。在修改完 - (UIInterfaceOrientationMask)supportedInterfaceOrientations方法后调用

  • 问题4:iOS 16 转屏后立即获取的设备方向不正确:[UIDevice currentDevice].orientation 返回 UIDeviceOrientationUnknown
    解决:延迟回调 complete 后获取


Codes:

NSString *MODeviceOrientationDescription(UIDeviceOrientation orientation) {
    switch (orientation) {
        case UIDeviceOrientationUnknown: return @"Unknown";
        case UIDeviceOrientationPortrait: return @"Portrait";
        case UIDeviceOrientationPortraitUpsideDown: return @"PortraitUpsideDown";
        case UIDeviceOrientationLandscapeLeft: return @"LandscapeLeft";
        case UIDeviceOrientationLandscapeRight: return @"LandscapeRight";
        case UIDeviceOrientationFaceUp: return @"FaceUp";
        case UIDeviceOrientationFaceDown: return @"FaceDown";
    }
}

NSString *MOInterfaceOrientationDescription(UIInterfaceOrientation orientation) {
    switch (orientation) {
        case UIInterfaceOrientationUnknown: return @"Unknown";
        case UIInterfaceOrientationPortrait: return @"Portrait";
        case UIInterfaceOrientationPortraitUpsideDown: return @"PortraitUpsideDown";
        case UIInterfaceOrientationLandscapeLeft: return @"LandscapeLeft";
        case UIInterfaceOrientationLandscapeRight: return @"LandscapeRight";
    }
}

typedef void(^MODeviceOrientationCompletion)(NSError * _Nullable error);

void MOGeometryUpdate(UIViewController *viewController,
                      UIDeviceOrientation orientation,
                      MODeviceOrientationCompletion completion) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000
    /* Preprocess macro for compiling on Xcode14 */
    if (@available(iOS 16.0, *)) {
        if (![viewController respondsToSelector:@selector(setNeedsUpdateOfSupportedInterfaceOrientations)]) {
            NSString *errMsg = @"viewController can't respond setNeedsUpdateOfSupportedInterfaceOrientations";
            if (completion) {
                completion([NSError errorWithDomain:@"MODemo" code:0 userInfo:@{NSLocalizedDescriptionKey: errMsg}]);
            }
            return;
        }
        [viewController setNeedsUpdateOfSupportedInterfaceOrientations];
        [viewController.navigationController setNeedsUpdateOfSupportedInterfaceOrientations];
        
        // find scene
        NSArray<UIScene *> *scenes = [[[UIApplication sharedApplication] connectedScenes] allObjects];
        __block UIScene *firstScene = scenes.firstObject;
        [scenes enumerateObjectsUsingBlock:^(UIScene * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            // 转屏需要使用 role = UIWindowSceneSessionRoleApplication 的Scene,当取到其他Scene时,会转屏失败
            if ([obj.session.role isEqualToString:UIWindowSceneSessionRoleApplication]) {
                firstScene = obj;
                *stop = YES;
            }
        }];
        if (![firstScene isKindOfClass:[UIWindowScene class]]) {
            NSString *errMsg = [NSString stringWithFormat:@"Unexpected first scene, scenes: %@", scenes];
            if (completion) {
                completion([NSError errorWithDomain:@"MODemo" code:0 userInfo:@{NSLocalizedDescriptionKey: errMsg}]);
            }
            return;
        }
        UIWindowScene *windowScene = (UIWindowScene *)firstScene;
        UIInterfaceOrientationMask mask = 1 << orientation;
        UIWindowSceneGeometryPreferencesIOS *preferences = [[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:mask];
        __block NSError *err = nil;
        [windowScene requestGeometryUpdateWithPreferences:preferences errorHandler:^(NSError * _Nonnull error) {
            NSString *errMsg = [NSString stringWithFormat:@"request geometry error: %@", error];
            err = [NSError errorWithDomain:@"MODemo" code:0 userInfo:@{NSLocalizedDescriptionKey: errMsg}];
            if (completion) {
                completion(error);
            }
        }];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (completion) {
                completion(err);
            }
        });
    } else {
        [[UIDevice currentDevice] setValue:@(orientation) forKey:@"orientation"];
        [UIViewController attemptRotationToDeviceOrientation];
        if (completion) {
            completion(nil);
        }
    }
#else
    /* Preprocess macro for compiling on Xcode13 */
    [[UIDevice currentDevice] setValue:@(orientation) forKey:@"orientation"];
    [UIViewController attemptRotationToDeviceOrientation];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        if (completion) {
            completion(nil);
        }
    });
#endif
}

github Demo


Reference:
Apple Developer Document
iOS 16适配屏幕旋转强制转屏切换大总结

你可能感兴趣的:(iOS开发,ios,cocoa,macos,objective-c)