Skip to content

Commit

Permalink
[ios]make the screenIfViewLoaded and windowSceneIfLoaded helpers reus…
Browse files Browse the repository at this point in the history
…able (flutter#44303)

The existing `screenIfViewLoaded` and `windowSceneIfLoaded` functions are private helpers of `FlutterViewController` class. This PR moves the logic to a category of the `UIViewController`, so it can be reused for any `UIViewController`s. 

*List which issues are fixed by this PR. You must list at least one issue.*

flutter#43972 (comment)

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
  • Loading branch information
hellohuanlin authored Aug 3, 2023
1 parent 9522ce9 commit 26b9d3b
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 49 deletions.
6 changes: 6 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,9 @@ ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/KeyCodeMap_I
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController_FlutterScreenAndSceneIfLoadedTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm + ../../../flutter/LICENSE
Expand Down Expand Up @@ -5395,6 +5398,9 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/KeyCodeMap_Int
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/UIViewController_FlutterScreenAndSceneIfLoadedTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/darwin/ios/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ source_set("flutter_framework_source") {
"framework/Source/KeyCodeMap_Internal.h",
"framework/Source/SemanticsObject.h",
"framework/Source/SemanticsObject.mm",
"framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h",
"framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm",
"framework/Source/accessibility_bridge.h",
"framework/Source/accessibility_bridge.mm",
"framework/Source/accessibility_text_entry.h",
Expand Down Expand Up @@ -298,6 +300,7 @@ shared_library("ios_test_flutter") {
"framework/Source/FlutterUndoManagerPluginTest.mm",
"framework/Source/FlutterViewControllerTest.mm",
"framework/Source/SemanticsObjectTest.mm",
"framework/Source/UIViewController_FlutterScreenAndSceneIfLoadedTest.mm",
"framework/Source/connection_collection_test.mm",
]
deps = [
Expand Down
3 changes: 2 additions & 1 deletion shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerDelegate.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterUndoManagerPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h"
Expand Down Expand Up @@ -880,7 +881,7 @@ - (BOOL)createShell:(NSString*)entrypoint

#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 13.0, *)) {
_isGpuDisabled = self.viewController.windowSceneIfViewLoaded.activationState ==
_isGpuDisabled = self.viewController.flutterWindowSceneIfViewLoaded.activationState ==
UISceneActivationStateBackground;
} else {
// [UIApplication sharedApplication API is not available for app extension.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "flutter/fml/logging.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"

namespace {

Expand Down Expand Up @@ -293,7 +294,7 @@ - (void)popSystemNavigator:(BOOL)isAnimated {
#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 15.0, *)) {
rootViewController =
[engineViewController windowSceneIfViewLoaded].keyWindow.rootViewController;
[engineViewController flutterWindowSceneIfViewLoaded].keyWindow.rootViewController;
} else {
FML_LOG(WARNING)
<< "rootViewController is not available in application extension prior to iOS 15.0.";
Expand Down
59 changes: 21 additions & 38 deletions shell/platform/darwin/ios/framework/Source/FlutterViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterView.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
Expand Down Expand Up @@ -516,25 +517,6 @@ - (void)loadView {
return pointer_data;
}

- (UIWindowScene*)windowSceneIfViewLoaded {
if (self.viewIfLoaded == nil) {
FML_LOG(WARNING) << "Trying to access the window scene before the view is loaded.";
return nil;
}
return self.viewIfLoaded.window.windowScene;
}

- (UIScreen*)screenIfViewLoaded {
if (@available(iOS 13.0, *)) {
if (self.viewIfLoaded == nil) {
FML_LOG(WARNING) << "Trying to access the screen before the view is loaded.";
return nil;
}
return [self windowSceneIfViewLoaded].screen;
}
return UIScreen.mainScreen;
}

static void SendFakeTouchEvent(UIScreen* screen,
FlutterEngine* engine,
CGPoint location,
Expand All @@ -554,7 +536,7 @@ - (BOOL)scrollViewShouldScrollToTop:(UIScrollView*)scrollView {
return NO;
}
CGPoint statusBarPoint = CGPointZero;
UIScreen* screen = [self screenIfViewLoaded];
UIScreen* screen = [self flutterScreenIfViewLoaded];
if (screen) {
SendFakeTouchEvent(screen, _engine.get(), statusBarPoint, flutter::PointerData::Change::kDown);
SendFakeTouchEvent(screen, _engine.get(), statusBarPoint, flutter::PointerData::Change::kUp);
Expand Down Expand Up @@ -869,8 +851,8 @@ - (void)viewDidAppear:(BOOL)animated {
BOOL stateIsActive = YES;
#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 13.0, *)) {
stateIsActive =
self.windowSceneIfViewLoaded.activationState == UISceneActivationStateForegroundActive;
stateIsActive = self.flutterWindowSceneIfViewLoaded.activationState ==
UISceneActivationStateForegroundActive;
}
#else
stateIsActive = UIApplication.sharedApplication.applicationState == UIApplicationStateActive;
Expand Down Expand Up @@ -1166,7 +1148,7 @@ - (void)dispatchTouches:(NSSet*)touches
// Activate or pause the correction of delivery frame rate of touch events.
[self triggerTouchRateCorrectionIfNeeded:touches];

const CGFloat scale = [self screenIfViewLoaded].scale;
const CGFloat scale = [self flutterScreenIfViewLoaded].scale;
auto packet =
std::make_unique<flutter::PointerDataPacket>(touches.count + touches_to_remove_count);

Expand Down Expand Up @@ -1381,7 +1363,7 @@ - (void)updateViewportMetricsIfNeeded {

- (void)viewDidLayoutSubviews {
CGRect viewBounds = self.view.bounds;
CGFloat scale = [self screenIfViewLoaded].scale;
CGFloat scale = [self flutterScreenIfViewLoaded].scale;

// Purposefully place this not visible.
_scrollView.get().frame = CGRectMake(0.0, 0.0, viewBounds.size.width, 0.0);
Expand All @@ -1400,8 +1382,8 @@ - (void)viewDidLayoutSubviews {
bool applicationOrSceneIsActive = YES;
#if APPLICATION_EXTENSION_API_ONLY
if (@available(iOS 13.0, *)) {
applicationOrSceneIsActive =
self.windowSceneIfViewLoaded.activationState == UISceneActivationStateForegroundActive;
applicationOrSceneIsActive = self.flutterWindowSceneIfViewLoaded.activationState ==
UISceneActivationStateForegroundActive;
}
#else
applicationOrSceneIsActive =
Expand Down Expand Up @@ -1436,7 +1418,7 @@ - (void)viewSafeAreaInsetsDidChange {

// Set _viewportMetrics physical size.
- (void)setViewportMetricsSize {
UIScreen* screen = [self screenIfViewLoaded];
UIScreen* screen = [self flutterScreenIfViewLoaded];
if (!screen) {
return;
}
Expand All @@ -1450,7 +1432,7 @@ - (void)setViewportMetricsSize {
//
// Viewport paddings represent the iOS safe area insets.
- (void)setViewportMetricsPaddings {
UIScreen* screen = [self screenIfViewLoaded];
UIScreen* screen = [self flutterScreenIfViewLoaded];
if (!screen) {
return;
}
Expand Down Expand Up @@ -1616,7 +1598,7 @@ - (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification
return FlutterKeyboardModeHidden;
}

CGRect screenRect = [self screenIfViewLoaded].bounds;
CGRect screenRect = [self flutterScreenIfViewLoaded].bounds;
CGRect adjustedKeyboardFrame = keyboardFrame;
adjustedKeyboardFrame.origin.y += [self calculateMultitaskingAdjustment:screenRect
keyboardFrame:keyboardFrame];
Expand Down Expand Up @@ -1656,7 +1638,7 @@ - (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGR
}
CGRect viewRectRelativeToScreen =
[self.viewIfLoaded convertRect:self.viewIfLoaded.frame
toCoordinateSpace:[self screenIfViewLoaded].coordinateSpace];
toCoordinateSpace:[self flutterScreenIfViewLoaded].coordinateSpace];
CGFloat viewBottom = CGRectGetMaxY(viewRectRelativeToScreen);
CGFloat offset = screenHeight - viewBottom;
if (offset > 0) {
Expand All @@ -1672,14 +1654,14 @@ - (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(NSInteger)
// Calculate how much of the keyboard intersects with the view.
CGRect viewRectRelativeToScreen =
[self.viewIfLoaded convertRect:self.viewIfLoaded.frame
toCoordinateSpace:[self screenIfViewLoaded].coordinateSpace];
toCoordinateSpace:[self flutterScreenIfViewLoaded].coordinateSpace];
CGRect intersection = CGRectIntersection(keyboardFrame, viewRectRelativeToScreen);
CGFloat portionOfKeyboardInView = CGRectGetHeight(intersection);

// The keyboard is treated as an inset since we want to effectively reduce the window size by
// the keyboard height. The Dart side will compute a value accounting for the keyboard-consuming
// bottom padding.
CGFloat scale = [self screenIfViewLoaded].scale;
CGFloat scale = [self flutterScreenIfViewLoaded].scale;
return portionOfKeyboardInView * scale;
}
return 0;
Expand Down Expand Up @@ -1974,8 +1956,9 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences {
if (@available(iOS 16.0, *)) {
NSSet<UIScene*>* scenes =
#if APPLICATION_EXTENSION_API_ONLY
self.windowSceneIfViewLoaded ? [NSSet setWithObject:self.windowSceneIfViewLoaded]
: [NSSet set];
self.flutterWindowSceneIfViewLoaded
? [NSSet setWithObject:self.flutterWindowSceneIfViewLoaded]
: [NSSet set];
#else
[UIApplication.sharedApplication.connectedScenes
filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(
Expand All @@ -1987,7 +1970,7 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences {
} else {
UIInterfaceOrientationMask currentInterfaceOrientation;
if (@available(iOS 13.0, *)) {
UIWindowScene* windowScene = [self windowSceneIfViewLoaded];
UIWindowScene* windowScene = [self flutterWindowSceneIfViewLoaded];
if (!windowScene) {
FML_LOG(WARNING)
<< "Accessing the interface orientation when the window scene is unavailable.";
Expand Down Expand Up @@ -2410,7 +2393,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer

- (void)hoverEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)) {
CGPoint location = [recognizer locationInView:self.view];
CGFloat scale = [self screenIfViewLoaded].scale;
CGFloat scale = [self flutterScreenIfViewLoaded].scale;
CGPoint oldLocation = _mouseState.location;
_mouseState.location = {location.x * scale, location.y * scale};

Expand Down Expand Up @@ -2467,7 +2450,7 @@ - (void)hoverEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4))

- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)) {
CGPoint translation = [recognizer translationInView:self.view];
const CGFloat scale = [self screenIfViewLoaded].scale;
const CGFloat scale = [self flutterScreenIfViewLoaded].scale;

translation.x *= scale;
translation.y *= scale;
Expand Down Expand Up @@ -2496,7 +2479,7 @@ - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(io

- (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)) {
CGPoint translation = [recognizer translationInView:self.view];
const CGFloat scale = [self screenIfViewLoaded].scale;
const CGFloat scale = [self flutterScreenIfViewLoaded].scale;

flutter::PointerData pointer_data = [self generatePointerDataAtLastMouseLocation];
pointer_data.device = reinterpret_cast<int64_t>(recognizer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
#import "flutter/shell/platform/embedder/embedder.h"
#import "flutter/third_party/spring_animation/spring_animation.h"

Expand Down Expand Up @@ -129,7 +130,6 @@ - (void)updateViewportMetricsIfNeeded;
- (void)onUserSettingsChanged:(NSNotification*)notification;
- (void)applicationWillTerminate:(NSNotification*)notification;
- (void)goToApplicationLifecycle:(nonnull NSString*)state;
- (UIScreen*)screenIfViewLoaded;
- (void)handleKeyboardNotification:(NSNotification*)notification;
- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(int)keyboardMode;
- (BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
Expand Down Expand Up @@ -199,7 +199,7 @@ - (id)setUpMockView:(FlutterViewController*)viewControllerMock
screen:(UIScreen*)screen
viewFrame:(CGRect)viewFrame
convertedFrame:(CGRect)convertedFrame {
OCMStub([viewControllerMock screenIfViewLoaded]).andReturn(screen);
OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
id mockView = OCMClassMock([UIView class]);
OCMStub([mockView frame]).andReturn(viewFrame);
OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg any]])
Expand Down Expand Up @@ -680,7 +680,7 @@ - (void)testCalculateKeyboardInset {
bundle:nil];
FlutterViewController* viewControllerMock = OCMPartialMock(viewController);
UIScreen* screen = [self setUpMockScreen];
OCMStub([viewControllerMock screenIfViewLoaded]).andReturn(screen);
OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);

CGFloat screenWidth = screen.bounds.size.width;
CGFloat screenHeight = screen.bounds.size.height;
Expand Down Expand Up @@ -943,7 +943,7 @@ - (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnored
bundle:nil];
FlutterViewController* viewControllerMock = OCMPartialMock(viewController);
UIScreen* screen = [self setUpMockScreen];
OCMStub([viewControllerMock screenIfViewLoaded]).andReturn(screen);
OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
mockEngine.viewController = viewController;

id mockCoordinator = OCMProtocolMock(@protocol(UIViewControllerTransitionCoordinator));
Expand All @@ -965,7 +965,7 @@ - (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
bundle:nil];
FlutterViewController* viewControllerMock = OCMPartialMock(viewController);
UIScreen* screen = [self setUpMockScreen];
OCMStub([viewControllerMock screenIfViewLoaded]).andReturn(screen);
OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
mockEngine.viewController = viewController;

// Mimic the device rotation with non-zero transition duration.
Expand Down Expand Up @@ -999,7 +999,7 @@ - (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
bundle:nil];
FlutterViewController* viewControllerMock = OCMPartialMock(viewController);
UIScreen* screen = [self setUpMockScreen];
OCMStub([viewControllerMock screenIfViewLoaded]).andReturn(screen);
OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
mockEngine.viewController = viewController;

// Mimic the device rotation with zero transition duration.
Expand Down Expand Up @@ -1512,7 +1512,7 @@ - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
if (@available(iOS 16.0, *)) {
mockWindowScene = OCMClassMock([UIWindowScene class]);
mockVC = OCMPartialMock(realVC);
OCMStub([mockVC windowSceneIfViewLoaded]).andReturn(mockWindowScene);
OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
if (realVC.supportedInterfaceOrientations == mask) {
OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
errorHandler:[OCMArg any]]);
Expand Down Expand Up @@ -1540,7 +1540,7 @@ - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
if (@available(iOS 13.0, *)) {
mockWindowScene = OCMClassMock([UIWindowScene class]);
mockVC = OCMPartialMock(realVC);
OCMStub([mockVC windowSceneIfViewLoaded]).andReturn(mockWindowScene);
OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation)
.andReturn(currentOrientation);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ typedef void (^FlutterKeyboardAnimationCallback)(fml::TimePoint);
- (void)addInternalPlugins;
- (void)deregisterNotifications;
- (int32_t)accessibilityFlags;
- (UIWindowScene*)windowSceneIfViewLoaded API_AVAILABLE(ios(13.0));

@end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_UIVIEWCONTROLLER_FLUTTERSCREENANDSCENEIFLOADED_H_
#define FLUTTER_UIVIEWCONTROLLER_FLUTTERSCREENANDSCENEIFLOADED_H_

#import <UIKit/UIKit.h>

@interface UIViewController (FlutterScreenAndSceneIfLoaded)

/// Returns a UIWindowScene if the UIViewController's view is loaded, and nil otherwise.
- (UIWindowScene*)flutterWindowSceneIfViewLoaded API_AVAILABLE(ios(13.0));

/// Before iOS 13, returns the main screen; After iOS 13, returns the screen the UIViewController is
/// attached to if its view is loaded, and nil otherwise.
- (UIScreen*)flutterScreenIfViewLoaded;

@end

#endif // FLUTTER_UIVIEWCONTROLLER_FLUTTERSCREENANDSCENEIFLOADED_H_
Loading

0 comments on commit 26b9d3b

Please sign in to comment.