diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 78195cf1800c8..45e90e0c76544 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -6,6 +6,7 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +#import #include #include "flutter/fml/memory/weak_ptr.h" @@ -1536,26 +1537,51 @@ - (void)onOrientationPreferencesUpdated:(NSNotification*)notification { - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { if (new_preferences != _orientationPreferences) { _orientationPreferences = new_preferences; - [UIViewController attemptRotationToDeviceOrientation]; - - UIInterfaceOrientationMask currentInterfaceOrientation = - 1 << [[UIApplication sharedApplication] statusBarOrientation]; - if (!(_orientationPreferences & currentInterfaceOrientation)) { - // Force orientation switch if the current orientation is not allowed - if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) { - // This is no official API but more like a workaround / hack (using - // key-value coding on a read-only property). This might break in - // the future, but currently it´s the only way to force an orientation change - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown) - forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) - forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { - [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) - forKey:@"orientation"]; + + if (@available(iOS 16.0, *)) { + for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) { + if (![scene isKindOfClass:[UIWindowScene class]]) { + continue; + } + UIWindowScene* windowScene = (UIWindowScene*)scene; + UIInterfaceOrientationMask currentInterfaceOrientation = + 1 << windowScene.interfaceOrientation; + if (!(_orientationPreferences & currentInterfaceOrientation)) { + [self setNeedsUpdateOfSupportedInterfaceOrientations]; + UIWindowSceneGeometryPreferencesIOS* preference = + [[UIWindowSceneGeometryPreferencesIOS alloc] + initWithInterfaceOrientations:_orientationPreferences]; + [windowScene + requestGeometryUpdateWithPreferences:preference + errorHandler:^(NSError* error) { + os_log_error(OS_LOG_DEFAULT, + "Failed to change device orientation: %@", + error); + }]; + } + } + } else { + UIInterfaceOrientationMask currentInterfaceOrientation = + 1 << [[UIApplication sharedApplication] statusBarOrientation]; + if (!(_orientationPreferences & currentInterfaceOrientation)) { + [UIViewController attemptRotationToDeviceOrientation]; + // Force orientation switch if the current orientation is not allowed + if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) { + // This is no official API but more like a workaround / hack (using + // key-value coding on a read-only property). This might break in + // the future, but currently it´s the only way to force an orientation change + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) + forKey:@"orientation"]; + } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { + [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) + forKey:@"orientation"]; + } } } } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index cbdcf3cf6974e..7ed4c0592e0d3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -888,22 +888,47 @@ - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask currentOrientation:(UIInterfaceOrientation)currentOrientation didChangeOrientation:(BOOL)didChange resultingOrientation:(UIInterfaceOrientation)resultingOrientation { - id deviceMock = OCMPartialMock([UIDevice currentDevice]); - if (!didChange) { - OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); + id mockApplication = OCMClassMock([UIApplication class]); + id mockWindowScene; + id deviceMock; + if (@available(iOS 16.0, *)) { + mockWindowScene = OCMClassMock([UIWindowScene class]); + OCMStub([mockWindowScene interfaceOrientation]).andReturn(currentOrientation); + if (!didChange) { + OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any] + errorHandler:[OCMArg any]]); + } else { + OCMExpect([mockWindowScene + requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^BOOL( + UIWindowSceneGeometryPreferencesIOS* + preferences) { + return preferences.interfaceOrientations == mask; + }] + errorHandler:[OCMArg any]]); + } + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]); } else { - OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); - } + deviceMock = OCMPartialMock([UIDevice currentDevice]); + if (!didChange) { + OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); + } else { + OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); + } + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); + } FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; - id mockApplication = OCMClassMock([UIApplication class]); - OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); - OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); [realVC performOrientationUpdate:mask]; - OCMVerifyAll(deviceMock); + if (@available(iOS 16.0, *)) { + OCMVerifyAll(mockWindowScene); + } else { + OCMVerifyAll(deviceMock); + } [deviceMock stopMocking]; [mockApplication stopMocking]; }