iOS_适配 iOS16 转屏
- 问题1:iOS 16 屏幕旋转报错:
[Orientation] BUG IN CLIENT OF UIKIT: Setting UIDevice.orientation is not supported. Please use UIWindowScene.requestGeometryUpdate(_:)
解决:iOS16 UIDevice 不再支持 setValue 方法,使用UIWindowScene
的requestGeometryUpdate()
的方法代替 - 问题2:
Xcode13
和Xcode14
编译出的安装包效果不一致 解决:需要区分编译环境,写两套代码 - 问题3:
- (BOOL)shouldAutorotate{}
在iOS 16 已经不起作用了,无论返回 yes or no 都能转屏。 解决:iOS16 新增:setNeedsUpdateOfSupportedInterfaceOrientations
方法,用于通知UIViewController
支持的屏幕方向有更新。在修改完- (UIInterfaceOrientationMask)supportedInterfaceOrientations
方法后调用 - 问题4:iOS 16 转屏后立即获取的设备方向不正确:
[UIDevice currentDevice].orientation
返回UIDeviceOrientationUnknown
解决:延迟回调 complete 后获取
Codes:
代码语言:javascript复制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适配屏幕旋转强制转屏切换大总结