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

Commit 19d8d3e

Browse files
committed
Use iOS 16 APIs to rotate orientation
1 parent 80847c9 commit 19d8d3e

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed

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

+37-16
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,46 @@ - (void)onOrientationPreferencesUpdated:(NSNotification*)notification {
15361537
- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences {
15371538
if (new_preferences != _orientationPreferences) {
15381539
_orientationPreferences = new_preferences;
1539-
[UIViewController attemptRotationToDeviceOrientation];
15401540

15411541
UIInterfaceOrientationMask currentInterfaceOrientation =
15421542
1 << [[UIApplication sharedApplication] statusBarOrientation];
15431543
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"];
1544+
if (@available(iOS 16.0, *)) {
1545+
[self setNeedsUpdateOfSupportedInterfaceOrientations];
1546+
for (UIScene* scene in UIApplication.sharedApplication.connectedScenes) {
1547+
if (![scene isKindOfClass:[UIWindowScene class]]) {
1548+
continue;
1549+
}
1550+
UIWindowSceneGeometryPreferencesIOS* preference =
1551+
[[UIWindowSceneGeometryPreferencesIOS alloc]
1552+
initWithInterfaceOrientations:_orientationPreferences];
1553+
[(UIWindowScene*)scene
1554+
requestGeometryUpdateWithPreferences:preference
1555+
errorHandler:^(NSError* error) {
1556+
os_log_error(OS_LOG_DEFAULT,
1557+
"Failed to change device orientation: %@",
1558+
error);
1559+
}];
1560+
}
1561+
} else {
1562+
[UIViewController attemptRotationToDeviceOrientation];
1563+
// Force orientation switch if the current orientation is not allowed
1564+
if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) {
1565+
// This is no official API but more like a workaround / hack (using
1566+
// key-value coding on a read-only property). This might break in
1567+
// the future, but currently it´s the only way to force an orientation change
1568+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait)
1569+
forKey:@"orientation"];
1570+
} else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) {
1571+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown)
1572+
forKey:@"orientation"];
1573+
} else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) {
1574+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft)
1575+
forKey:@"orientation"];
1576+
} else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) {
1577+
[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight)
1578+
forKey:@"orientation"];
1579+
}
15591580
}
15601581
}
15611582
}

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

+33-9
Original file line numberDiff line numberDiff line change
@@ -888,22 +888,46 @@ - (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 windowSceneMock;
893+
id deviceMock;
894+
if (@available(iOS 16.0, *)) {
895+
windowSceneMock = OCMClassMock([UIWindowScene class]);
896+
if (!didChange) {
897+
OCMReject([windowSceneMock requestGeometryUpdateWithPreferences:[OCMArg any]
898+
errorHandler:[OCMArg any]]);
899+
} else {
900+
OCMExpect([windowSceneMock
901+
requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^BOOL(
902+
UIWindowSceneGeometryPreferencesIOS*
903+
preferences) {
904+
return preferences.interfaceOrientations == mask;
905+
}]
906+
errorHandler:[OCMArg any]]);
907+
}
908+
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
909+
OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:windowSceneMock]);
894910
} else {
895-
OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]);
896-
}
911+
deviceMock = OCMPartialMock([UIDevice currentDevice]);
912+
if (!didChange) {
913+
OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]);
914+
} else {
915+
OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]);
916+
}
897917

918+
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
919+
OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
920+
}
898921
FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine
899922
nibName:nil
900923
bundle:nil];
901-
id mockApplication = OCMClassMock([UIApplication class]);
902-
OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
903-
OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
904924

905925
[realVC performOrientationUpdate:mask];
906-
OCMVerifyAll(deviceMock);
926+
if (@available(iOS 16.0, *)) {
927+
OCMVerifyAll(windowSceneMock);
928+
} else {
929+
OCMVerifyAll(deviceMock);
930+
}
907931
[deviceMock stopMocking];
908932
[mockApplication stopMocking];
909933
}

0 commit comments

Comments
 (0)