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

Commit 1c13aba

Browse files
author
Chris Yang
authored
The ForwardingGestureRecognizers to have back reference to the PlatformViewsController so it can access FlutterViewController when its available (#20708)
1 parent 9939c26 commit 1c13aba

File tree

4 files changed

+128
-38
lines changed

4 files changed

+128
-38
lines changed

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

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@
107107
flutter_view_controller_.reset([flutter_view_controller retain]);
108108
}
109109

110+
UIViewController* FlutterPlatformViewsController::getFlutterViewController() {
111+
return flutter_view_controller_.get();
112+
}
113+
110114
void FlutterPlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult& result) {
111115
if ([[call method] isEqualToString:@"create"]) {
112116
OnCreate(call, result);
@@ -161,7 +165,7 @@
161165

162166
FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc]
163167
initWithEmbeddedView:embedded_view.view
164-
flutterViewController:flutter_view_controller_.get()
168+
platformViewsController:GetWeakPtr()
165169
gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies[viewType]]
166170
autorelease];
167171

@@ -701,15 +705,17 @@ - (instancetype)initWithTarget:(id)target
701705
// directly to the FlutterView.
702706
@interface ForwardingGestureRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate>
703707
- (instancetype)initWithTarget:(id)target
704-
flutterViewController:(UIViewController*)flutterViewController;
708+
platformViewsController:
709+
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController;
705710
@end
706711

707712
@implementation FlutterTouchInterceptingView {
708713
fml::scoped_nsobject<DelayingGestureRecognizer> _delayingRecognizer;
709714
FlutterPlatformViewGestureRecognizersBlockingPolicy _blockingPolicy;
710715
}
711716
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
712-
flutterViewController:(UIViewController*)flutterViewController
717+
platformViewsController:
718+
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController
713719
gestureRecognizersBlockingPolicy:
714720
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy {
715721
self = [super initWithFrame:embeddedView.frame];
@@ -720,9 +726,9 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView
720726

721727
[self addSubview:embeddedView];
722728

723-
ForwardingGestureRecognizer* forwardingRecognizer =
724-
[[[ForwardingGestureRecognizer alloc] initWithTarget:self
725-
flutterViewController:flutterViewController] autorelease];
729+
ForwardingGestureRecognizer* forwardingRecognizer = [[[ForwardingGestureRecognizer alloc]
730+
initWithTarget:self
731+
platformViewsController:std::move(platformViewsController)] autorelease];
726732

727733
_delayingRecognizer.reset([[DelayingGestureRecognizer alloc]
728734
initWithTarget:self
@@ -833,39 +839,42 @@ - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
833839
@end
834840

835841
@implementation ForwardingGestureRecognizer {
836-
// We can't dispatch events to the framework without this back pointer.
837-
// This is a weak reference, the ForwardingGestureRecognizer is owned by the
838-
// FlutterTouchInterceptingView which is strong referenced only by the FlutterView,
839-
// which is strongly referenced by the FlutterViewController.
840-
// So this is safe as when FlutterView is deallocated the reference to ForwardingGestureRecognizer
841-
// will go away.
842-
UIViewController* _flutterViewController;
842+
// Weak reference to FlutterPlatformViewsController. The FlutterPlatformViewsController has
843+
// a reference to the FlutterViewController, where we can dispatch pointer events to.
844+
//
845+
// The lifecycle of FlutterPlatformViewsController is bind to FlutterEngine, which should always
846+
// outlives the FlutterViewController. And ForwardingGestureRecognizer is owned by a subview of
847+
// FlutterView, so the ForwardingGestureRecognizer never out lives FlutterViewController.
848+
// Therefore, `_platformViewsController` should never be nullptr.
849+
fml::WeakPtr<flutter::FlutterPlatformViewsController> _platformViewsController;
843850
// Counting the pointers that has started in one touch sequence.
844851
NSInteger _currentTouchPointersCount;
845852
}
846853

847854
- (instancetype)initWithTarget:(id)target
848-
flutterViewController:(UIViewController*)flutterViewController {
855+
platformViewsController:
856+
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController {
849857
self = [super initWithTarget:target action:nil];
850858
if (self) {
851859
self.delegate = self;
852-
_flutterViewController = flutterViewController;
860+
FML_DCHECK(platformViewsController.get() != nullptr);
861+
_platformViewsController = std::move(platformViewsController);
853862
_currentTouchPointersCount = 0;
854863
}
855864
return self;
856865
}
857866

858867
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
859-
[_flutterViewController touchesBegan:touches withEvent:event];
868+
[_platformViewsController.get()->getFlutterViewController() touchesBegan:touches withEvent:event];
860869
_currentTouchPointersCount += touches.count;
861870
}
862871

863872
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
864-
[_flutterViewController touchesMoved:touches withEvent:event];
873+
[_platformViewsController.get()->getFlutterViewController() touchesMoved:touches withEvent:event];
865874
}
866875

867876
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
868-
[_flutterViewController touchesEnded:touches withEvent:event];
877+
[_platformViewsController.get()->getFlutterViewController() touchesEnded:touches withEvent:event];
869878
_currentTouchPointersCount -= touches.count;
870879
// Touches in one touch sequence are sent to the touchesEnded method separately if different
871880
// fingers stop touching the screen at different time. So one touchesEnded method triggering does
@@ -877,7 +886,8 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
877886
}
878887

879888
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
880-
[_flutterViewController touchesCancelled:touches withEvent:event];
889+
[_platformViewsController.get()->getFlutterViewController() touchesCancelled:touches
890+
withEvent:event];
881891
_currentTouchPointersCount = 0;
882892
self.state = UIGestureRecognizerStateFailed;
883893
}

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
88
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
99
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
10+
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
1011
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
12+
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
1113
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
1214
#import "third_party/ocmock/Source/OCMock/OCMock.h"
1315

@@ -508,6 +510,70 @@ - (void)testClipPath {
508510
flutterPlatformViewsController->Reset();
509511
}
510512

513+
- (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
514+
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
515+
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
516+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
517+
/*platform=*/thread_task_runner,
518+
/*raster=*/thread_task_runner,
519+
/*ui=*/thread_task_runner,
520+
/*io=*/thread_task_runner);
521+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
522+
/*delegate=*/mock_delegate,
523+
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
524+
/*task_runners=*/runners);
525+
526+
auto flutterPlatformViewsController = std::make_unique<flutter::FlutterPlatformViewsController>();
527+
528+
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
529+
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
530+
flutterPlatformViewsController->RegisterViewFactory(
531+
factory, @"MockFlutterPlatformView",
532+
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
533+
FlutterResult result = ^(id result) {
534+
};
535+
flutterPlatformViewsController->OnMethodCall(
536+
[FlutterMethodCall
537+
methodCallWithMethodName:@"create"
538+
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
539+
result);
540+
541+
XCTAssertNotNil(gMockPlatformView);
542+
543+
// Find touch inteceptor view
544+
UIView* touchInteceptorView = gMockPlatformView;
545+
while (touchInteceptorView != nil &&
546+
![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
547+
touchInteceptorView = touchInteceptorView.superview;
548+
}
549+
XCTAssertNotNil(touchInteceptorView);
550+
551+
// Find ForwardGestureRecognizer
552+
UIGestureRecognizer* forwardGectureRecognizer = nil;
553+
for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
554+
if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
555+
forwardGectureRecognizer = gestureRecognizer;
556+
break;
557+
}
558+
}
559+
560+
// Before setting flutter view controller, events are not dispatched.
561+
NSSet* touches1 = OCMClassMock([NSSet class]);
562+
UIEvent* event1 = OCMClassMock([UIEvent class]);
563+
UIViewController* mockFlutterViewContoller = OCMClassMock([UIViewController class]);
564+
[forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
565+
OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
566+
567+
// Set flutter view controller allows events to be dispatched.
568+
NSSet* touches2 = OCMClassMock([NSSet class]);
569+
UIEvent* event2 = OCMClassMock([UIEvent class]);
570+
flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
571+
[forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
572+
OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]);
573+
574+
flutterPlatformViewsController->Reset();
575+
}
576+
511577
- (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
512578
unsigned char pixel[4] = {0};
513579

shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include "flutter/shell/platform/darwin/ios/ios_context.h"
1717
#include "third_party/skia/include/core/SkPictureRecorder.h"
1818

19+
@class FlutterTouchInterceptingView;
20+
1921
// A UIView that acts as a clipping mask for the |ChildClippingView|.
2022
//
2123
// On the [UIView drawRect:] method, this view performs a series of clipping operations and sets the
@@ -43,24 +45,6 @@
4345

4446
@end
4547

46-
// A UIView that is used as the parent for embedded UIViews.
47-
//
48-
// This view has 2 roles:
49-
// 1. Delay or prevent touch events from arriving the embedded view.
50-
// 2. Dispatching all events that are hittested to the embedded view to the FlutterView.
51-
@interface FlutterTouchInterceptingView : UIView
52-
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
53-
flutterViewController:(UIViewController*)flutterViewController
54-
gestureRecognizersBlockingPolicy:
55-
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy;
56-
57-
// Stop delaying any active touch sequence (and let it arrive the embedded view).
58-
- (void)releaseGesture;
59-
60-
// Prevent the touch sequence from ever arriving to the embedded view.
61-
- (void)blockGesture;
62-
@end
63-
6448
// The parent view handles clipping to its subviews.
6549
@interface ChildClippingView : UIView
6650

@@ -143,10 +127,14 @@ class FlutterPlatformViewsController {
143127

144128
~FlutterPlatformViewsController();
145129

130+
fml::WeakPtr<flutter::FlutterPlatformViewsController> GetWeakPtr();
131+
146132
void SetFlutterView(UIView* flutter_view);
147133

148134
void SetFlutterViewController(UIViewController* flutter_view_controller);
149135

136+
UIViewController* getFlutterViewController();
137+
150138
void RegisterViewFactory(
151139
NSObject<FlutterPlatformViewFactory>* factory,
152140
NSString* factoryId,
@@ -255,6 +243,8 @@ class FlutterPlatformViewsController {
255243
std::map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>
256244
gesture_recognizers_blocking_policies;
257245

246+
std::unique_ptr<fml::WeakPtrFactory<FlutterPlatformViewsController>> weak_factory_;
247+
258248
void OnCreate(FlutterMethodCall* call, FlutterResult& result);
259249
void OnDispose(FlutterMethodCall* call, FlutterResult& result);
260250
void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result);
@@ -313,4 +303,23 @@ class FlutterPlatformViewsController {
313303

314304
} // namespace flutter
315305

306+
// A UIView that is used as the parent for embedded UIViews.
307+
//
308+
// This view has 2 roles:
309+
// 1. Delay or prevent touch events from arriving the embedded view.
310+
// 2. Dispatching all events that are hittested to the embedded view to the FlutterView.
311+
@interface FlutterTouchInterceptingView : UIView
312+
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
313+
platformViewsController:
314+
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController
315+
gestureRecognizersBlockingPolicy:
316+
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy;
317+
318+
// Stop delaying any active touch sequence (and let it arrive the embedded view).
319+
- (void)releaseGesture;
320+
321+
// Prevent the touch sequence from ever arriving to the embedded view.
322+
- (void)blockGesture;
323+
@end
324+
316325
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,15 @@
2424
FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default;
2525

2626
FlutterPlatformViewsController::FlutterPlatformViewsController()
27-
: layer_pool_(std::make_unique<FlutterPlatformViewLayerPool>()){};
27+
: layer_pool_(std::make_unique<FlutterPlatformViewLayerPool>()),
28+
weak_factory_(std::make_unique<fml::WeakPtrFactory<FlutterPlatformViewsController>>(this)){};
2829

2930
FlutterPlatformViewsController::~FlutterPlatformViewsController() = default;
3031

32+
fml::WeakPtr<flutter::FlutterPlatformViewsController> FlutterPlatformViewsController::GetWeakPtr() {
33+
return weak_factory_->GetWeakPtr();
34+
}
35+
3136
CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) {
3237
// Skia only supports 2D transform so we don't map z.
3338
CATransform3D transform = CATransform3DIdentity;

0 commit comments

Comments
 (0)