Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 69f2bc8

Browse files
authored
Use iOS 16 APIs to rotate orientation (#36874)
1 parent 791221c commit 69f2bc8

File tree

2 files changed

+80
-29
lines changed

2 files changed

+80
-29
lines changed

shell/platform/darwin/ios/framework/Source/FlutterViewController.mm

+46-20
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
88

9+
#import <os/log.h>
910
#include <memory>
1011

1112
#include "flutter/fml/memory/weak_ptr.h"
@@ -1536,26 +1537,51 @@ - (void)onOrientationPreferencesUpdated:(NSNotification*)notification {
15361537
- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences {
15371538
if (new_preferences != _orientationPreferences) {
15381539
_orientationPreferences = new_preferences;
1539-
[UIViewController attemptRotationToDeviceOrientation];
1540-
1541-
UIInterfaceOrientationMask currentInterfaceOrientation =
1542-
1 << [[UIApplication sharedApplication] statusBarOrientation];
1543-
if (!(_orientationPreferences & currentInterfaceOrientation)) {
1544-
// Force orientation switch if the current orientation is not allowed
1545-
if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) {
1546-
// This is no official API but more like a workaround / hack (using
1547-
// key-value coding on a read-only property). This might break in
1548-
// the future, but currently it´s the only way to force an orientation change
1549-
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
1550-
} else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) {
1551-
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown)
1552-
forKey:@"orientation"];
1553-
} else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) {
1554-
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft)
1555-
forKey:@"orientation"];
1556-
} else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) {
1557-
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight)
1558-
forKey:@"orientation"];
1540+
1541+
if (@available(iOS 16.0, *)) {
1542+
for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) {
1543+
if (![scene isKindOfClass:[UIWindowScene class]]) {
1544+
continue;
1545+
}
1546+
UIWindowScene* windowScene = (UIWindowScene*)scene;
1547+
UIInterfaceOrientationMask currentInterfaceOrientation =
1548+
1 << windowScene.interfaceOrientation;
1549+
if (!(_orientationPreferences & currentInterfaceOrientation)) {
1550+
[self setNeedsUpdateOfSupportedInterfaceOrientations];
1551+
UIWindowSceneGeometryPreferencesIOS* preference =
1552+
[[UIWindowSceneGeometryPreferencesIOS alloc]
1553+
initWithInterfaceOrientations:_orientationPreferences];
1554+
[windowScene
1555+
requestGeometryUpdateWithPreferences:preference
1556+
errorHandler:^(NSError* error) {
1557+
os_log_error(OS_LOG_DEFAULT,
1558+
"Failed to change device orientation: %@",
1559+
error);
1560+
}];
1561+
}
1562+
}
1563+
} else {
1564+
UIInterfaceOrientationMask currentInterfaceOrientation =
1565+
1 << [[UIApplication sharedApplication] statusBarOrientation];
1566+
if (!(_orientationPreferences & currentInterfaceOrientation)) {
1567+
[UIViewController attemptRotationToDeviceOrientation];
1568+
// Force orientation switch if the current orientation is not allowed
1569+
if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) {
1570+
// This is no official API but more like a workaround / hack (using
1571+
// key-value coding on a read-only property). This might break in
1572+
// the future, but currently it´s the only way to force an orientation change
1573+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait)
1574+
forKey:@"orientation"];
1575+
} else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) {
1576+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown)
1577+
forKey:@"orientation"];
1578+
} else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) {
1579+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft)
1580+
forKey:@"orientation"];
1581+
} else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) {
1582+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight)
1583+
forKey:@"orientation"];
1584+
}
15591585
}
15601586
}
15611587
}

shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm

+34-9
Original file line numberDiff line numberDiff line change
@@ -888,22 +888,47 @@ - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
888888
currentOrientation:(UIInterfaceOrientation)currentOrientation
889889
didChangeOrientation:(BOOL)didChange
890890
resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
891-
id deviceMock = OCMPartialMock([UIDevice currentDevice]);
892-
if (!didChange) {
893-
OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]);
891+
id mockApplication = OCMClassMock([UIApplication class]);
892+
id mockWindowScene;
893+
id deviceMock;
894+
if (@available(iOS 16.0, *)) {
895+
mockWindowScene = OCMClassMock([UIWindowScene class]);
896+
OCMStub([mockWindowScene interfaceOrientation]).andReturn(currentOrientation);
897+
if (!didChange) {
898+
OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
899+
errorHandler:[OCMArg any]]);
900+
} else {
901+
OCMExpect([mockWindowScene
902+
requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^BOOL(
903+
UIWindowSceneGeometryPreferencesIOS*
904+
preferences) {
905+
return preferences.interfaceOrientations == mask;
906+
}]
907+
errorHandler:[OCMArg any]]);
908+
}
909+
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
910+
OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
894911
} else {
895-
OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]);
896-
}
912+
deviceMock = OCMPartialMock([UIDevice currentDevice]);
913+
if (!didChange) {
914+
OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]);
915+
} else {
916+
OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]);
917+
}
897918

919+
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
920+
OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
921+
}
898922
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine
899923
nibName:nil
900924
bundle:nil];
901-
id mockApplication = OCMClassMock([UIApplication class]);
902-
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
903-
OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
904925

905926
[realVC performOrientationUpdate:mask];
906-
OCMVerifyAll(deviceMock);
927+
if (@available(iOS 16.0, *)) {
928+
OCMVerifyAll(mockWindowScene);
929+
} else {
930+
OCMVerifyAll(deviceMock);
931+
}
907932
[deviceMock stopMocking];
908933
[mockApplication stopMocking];
909934
}

0 commit comments

Comments
 (0)