iOS 16 屏幕旋转适配
本文主要解决这个问题:Error Domain=UISceneErrorDomain Code=101 "None of the requested orientations are supported by the view controller. Requested: landscapeRight; Supported: portrait"
关于报错
Error Domain=UISceneErrorDomain Code=101 "None of the requested orientations are supported by the view controller. Requested: landscapeRight; Supported: portrait"
这个报错卡了很久,网上也没搜到相应的解决方案。plist 文件也检查过了,设置了多个方向,还是报错。
核心解决点有两点:
- 在iOS 16上 需要完成 AppDelegate 内的回调
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
众所周知,这个函数的优先级是高于plist的。 - 在iOS 16屏幕旋转前,调用
UIViewController.attemptRotationToDeviceOrientation()
, 这点很重要,报错不支持旋转的方向,就是因为没有调用这个函数引起的。
iOS 16 屏幕旋转通知
看到网上有人说屏幕旋转通知收不到了,其实是可以的。只是需要开启和关闭监听。
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(screenChangedOrientation(_:)), name: UIDevice.orientationDidChangeNotification, object: nil)
UIDevice.current.endGeneratingDeviceOrientationNotifications()
核心代码
ViewController 中
/// 强制转横坚屏操作
/// - Parameter landscape: 是否转横屏
private func forceDeviceRotaiton(landscape: Bool) {
...
...
guard #available(iOS 16.0, *) else {
if landscape {
UIDevice.current.setValue(NSNumber.init(value: UIInterfaceOrientation.unknown.rawValue), forKey: "orientation")
UIDevice.current.setValue(NSNumber.init(value: UIInterfaceOrientation.landscapeRight.rawValue), forKey: "orientation")
} else {
UIDevice.current.setValue(NSNumber.init(value: UIInterfaceOrientation.unknown.rawValue), forKey: "orientation")
UIDevice.current.setValue(NSNumber.init(value: UIInterfaceOrientation.portrait.rawValue), forKey: "orientation")
}
return
}
switchMode(full: landscape)
}
@available(iOS 16.0, *)
private func switchMode(full: Bool) {
DispatchQueue.main.async {
guard
let scence = UIApplication.shared.connectedScenes.first as? UIWindowScene
else {
return
}
NotificationCenter.default.post(name: NSNotification.Name("Notification_isLandscape"), object: NSNumber.init(booleanLiteral: full))
UIViewController.attemptRotationToDeviceOrientation()
let orientation: UIInterfaceOrientationMask = full ? .landscapeRight : .portrait
let geometryPreferencesIOS = UIWindowScene.GeometryPreferences.iOS(interfaceOrientations: orientation)
scence.requestGeometryUpdate(geometryPreferencesIOS) { error in
print("debug \(error)")
}
self.navigationController?.topViewController?.setNeedsUpdateOfSupportedInterfaceOrientations()
}
}
AppDelegate 中
#import <ReactiveObjC/ReactiveObjC.h>
@property (nonatomic, assign) BOOL isLandscape;
- (void)applicationForSafeMode:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
@weakify(self)
[[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"Notification_isLandscape" object:nil] takeUntil:[self rac_willDeallocSignal]] subscribeNext:^(NSNotification * _Nullable sender) {
@strongify(self)
NSNumber *notification_isLandscape = sender.object;
self.isLandscape = notification_isLandscape.boolValue;
}];
self.isLandscape = NO;
}
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
if (@available(iOS 16.0, *)) {
if(self.isLandscape == YES){
return UIInterfaceOrientationMaskLandscape;
}
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
if(self.isLandscape){
if (@available(iOS 16.0, *)) {
[UIViewController attemptRotationToDeviceOrientation];
[[NSNotificationCenter defaultCenter] postNotificationName:@"Notification_isLandscape" object:@(YES)];
NSArray *array = [[UIApplication sharedApplication].connectedScenes allObjects];
UIWindowScene *scene = (UIWindowScene *)[array firstObject];
UIWindowSceneGeometryPreferencesIOS *geometryPreferences = [[UIWindowSceneGeometryPreferencesIOS alloc] initWithInterfaceOrientations:UIInterfaceOrientationMaskLandscape];
[scene requestGeometryUpdateWithPreferences:geometryPreferences errorHandler:^(NSError * _Nonnull error) {}];
}
}
...
}