The ForwardingGestureRecognizers to have back reference to the PlatformViewsController so it can access FlutterViewController when its available (#20708)

This commit is contained in:
Chris Yang 2020-08-24 12:41:02 -07:00 committed by GitHub
parent 9939c26bd2
commit 1c13abaab1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 38 deletions

View File

@ -107,6 +107,10 @@ void FlutterPlatformViewsController::SetFlutterViewController(
flutter_view_controller_.reset([flutter_view_controller retain]);
}
UIViewController* FlutterPlatformViewsController::getFlutterViewController() {
return flutter_view_controller_.get();
}
void FlutterPlatformViewsController::OnMethodCall(FlutterMethodCall* call, FlutterResult& result) {
if ([[call method] isEqualToString:@"create"]) {
OnCreate(call, result);
@ -161,7 +165,7 @@ void FlutterPlatformViewsController::OnCreate(FlutterMethodCall* call, FlutterRe
FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc]
initWithEmbeddedView:embedded_view.view
flutterViewController:flutter_view_controller_.get()
platformViewsController:GetWeakPtr()
gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies[viewType]]
autorelease];
@ -701,7 +705,8 @@ void FlutterPlatformViewsController::DisposeViews() {
// directly to the FlutterView.
@interface ForwardingGestureRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate>
- (instancetype)initWithTarget:(id)target
flutterViewController:(UIViewController*)flutterViewController;
platformViewsController:
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController;
@end
@implementation FlutterTouchInterceptingView {
@ -709,7 +714,8 @@ void FlutterPlatformViewsController::DisposeViews() {
FlutterPlatformViewGestureRecognizersBlockingPolicy _blockingPolicy;
}
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
flutterViewController:(UIViewController*)flutterViewController
platformViewsController:
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy {
self = [super initWithFrame:embeddedView.frame];
@ -720,9 +726,9 @@ void FlutterPlatformViewsController::DisposeViews() {
[self addSubview:embeddedView];
ForwardingGestureRecognizer* forwardingRecognizer =
[[[ForwardingGestureRecognizer alloc] initWithTarget:self
flutterViewController:flutterViewController] autorelease];
ForwardingGestureRecognizer* forwardingRecognizer = [[[ForwardingGestureRecognizer alloc]
initWithTarget:self
platformViewsController:std::move(platformViewsController)] autorelease];
_delayingRecognizer.reset([[DelayingGestureRecognizer alloc]
initWithTarget:self
@ -833,39 +839,42 @@ void FlutterPlatformViewsController::DisposeViews() {
@end
@implementation ForwardingGestureRecognizer {
// We can't dispatch events to the framework without this back pointer.
// This is a weak reference, the ForwardingGestureRecognizer is owned by the
// FlutterTouchInterceptingView which is strong referenced only by the FlutterView,
// which is strongly referenced by the FlutterViewController.
// So this is safe as when FlutterView is deallocated the reference to ForwardingGestureRecognizer
// will go away.
UIViewController* _flutterViewController;
// Weak reference to FlutterPlatformViewsController. The FlutterPlatformViewsController has
// a reference to the FlutterViewController, where we can dispatch pointer events to.
//
// The lifecycle of FlutterPlatformViewsController is bind to FlutterEngine, which should always
// outlives the FlutterViewController. And ForwardingGestureRecognizer is owned by a subview of
// FlutterView, so the ForwardingGestureRecognizer never out lives FlutterViewController.
// Therefore, `_platformViewsController` should never be nullptr.
fml::WeakPtr<flutter::FlutterPlatformViewsController> _platformViewsController;
// Counting the pointers that has started in one touch sequence.
NSInteger _currentTouchPointersCount;
}
- (instancetype)initWithTarget:(id)target
flutterViewController:(UIViewController*)flutterViewController {
platformViewsController:
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController {
self = [super initWithTarget:target action:nil];
if (self) {
self.delegate = self;
_flutterViewController = flutterViewController;
FML_DCHECK(platformViewsController.get() != nullptr);
_platformViewsController = std::move(platformViewsController);
_currentTouchPointersCount = 0;
}
return self;
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[_flutterViewController touchesBegan:touches withEvent:event];
[_platformViewsController.get()->getFlutterViewController() touchesBegan:touches withEvent:event];
_currentTouchPointersCount += touches.count;
}
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
[_flutterViewController touchesMoved:touches withEvent:event];
[_platformViewsController.get()->getFlutterViewController() touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
[_flutterViewController touchesEnded:touches withEvent:event];
[_platformViewsController.get()->getFlutterViewController() touchesEnded:touches withEvent:event];
_currentTouchPointersCount -= touches.count;
// Touches in one touch sequence are sent to the touchesEnded method separately if different
// fingers stop touching the screen at different time. So one touchesEnded method triggering does
@ -877,7 +886,8 @@ void FlutterPlatformViewsController::DisposeViews() {
}
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
[_flutterViewController touchesCancelled:touches withEvent:event];
[_platformViewsController.get()->getFlutterViewController() touchesCancelled:touches
withEvent:event];
_currentTouchPointersCount = 0;
self.state = UIGestureRecognizerStateFailed;
}

View File

@ -7,7 +7,9 @@
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h"
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h"
#import "flutter/shell/platform/darwin/ios/platform_view_ios.h"
#import "third_party/ocmock/Source/OCMock/OCMock.h"
@ -508,6 +510,70 @@ fml::RefPtr<fml::TaskRunner> CreateNewThread(std::string name) {
flutterPlatformViewsController->Reset();
}
- (void)testSetFlutterViewControllerAfterCreateCanStillDispatchTouchEvents {
flutter::FlutterPlatformViewsTestMockPlatformViewDelegate mock_delegate;
auto thread_task_runner = CreateNewThread("FlutterPlatformViewsTest");
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
/*platform=*/thread_task_runner,
/*raster=*/thread_task_runner,
/*ui=*/thread_task_runner,
/*io=*/thread_task_runner);
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
/*delegate=*/mock_delegate,
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
/*task_runners=*/runners);
auto flutterPlatformViewsController = std::make_unique<flutter::FlutterPlatformViewsController>();
FlutterPlatformViewsTestMockFlutterPlatformFactory* factory =
[[FlutterPlatformViewsTestMockFlutterPlatformFactory new] autorelease];
flutterPlatformViewsController->RegisterViewFactory(
factory, @"MockFlutterPlatformView",
FlutterPlatformViewGestureRecognizersBlockingPolicyEager);
FlutterResult result = ^(id result) {
};
flutterPlatformViewsController->OnMethodCall(
[FlutterMethodCall
methodCallWithMethodName:@"create"
arguments:@{@"id" : @2, @"viewType" : @"MockFlutterPlatformView"}],
result);
XCTAssertNotNil(gMockPlatformView);
// Find touch inteceptor view
UIView* touchInteceptorView = gMockPlatformView;
while (touchInteceptorView != nil &&
![touchInteceptorView isKindOfClass:[FlutterTouchInterceptingView class]]) {
touchInteceptorView = touchInteceptorView.superview;
}
XCTAssertNotNil(touchInteceptorView);
// Find ForwardGestureRecognizer
UIGestureRecognizer* forwardGectureRecognizer = nil;
for (UIGestureRecognizer* gestureRecognizer in touchInteceptorView.gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:NSClassFromString(@"ForwardingGestureRecognizer")]) {
forwardGectureRecognizer = gestureRecognizer;
break;
}
}
// Before setting flutter view controller, events are not dispatched.
NSSet* touches1 = OCMClassMock([NSSet class]);
UIEvent* event1 = OCMClassMock([UIEvent class]);
UIViewController* mockFlutterViewContoller = OCMClassMock([UIViewController class]);
[forwardGectureRecognizer touchesBegan:touches1 withEvent:event1];
OCMReject([mockFlutterViewContoller touchesBegan:touches1 withEvent:event1]);
// Set flutter view controller allows events to be dispatched.
NSSet* touches2 = OCMClassMock([NSSet class]);
UIEvent* event2 = OCMClassMock([UIEvent class]);
flutterPlatformViewsController->SetFlutterViewController(mockFlutterViewContoller);
[forwardGectureRecognizer touchesBegan:touches2 withEvent:event2];
OCMVerify([mockFlutterViewContoller touchesBegan:touches2 withEvent:event2]);
flutterPlatformViewsController->Reset();
}
- (int)alphaOfPoint:(CGPoint)point onView:(UIView*)view {
unsigned char pixel[4] = {0};

View File

@ -16,6 +16,8 @@
#include "flutter/shell/platform/darwin/ios/ios_context.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
@class FlutterTouchInterceptingView;
// A UIView that acts as a clipping mask for the |ChildClippingView|.
//
// On the [UIView drawRect:] method, this view performs a series of clipping operations and sets the
@ -43,24 +45,6 @@
@end
// A UIView that is used as the parent for embedded UIViews.
//
// This view has 2 roles:
// 1. Delay or prevent touch events from arriving the embedded view.
// 2. Dispatching all events that are hittested to the embedded view to the FlutterView.
@interface FlutterTouchInterceptingView : UIView
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
flutterViewController:(UIViewController*)flutterViewController
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy;
// Stop delaying any active touch sequence (and let it arrive the embedded view).
- (void)releaseGesture;
// Prevent the touch sequence from ever arriving to the embedded view.
- (void)blockGesture;
@end
// The parent view handles clipping to its subviews.
@interface ChildClippingView : UIView
@ -143,10 +127,14 @@ class FlutterPlatformViewsController {
~FlutterPlatformViewsController();
fml::WeakPtr<flutter::FlutterPlatformViewsController> GetWeakPtr();
void SetFlutterView(UIView* flutter_view);
void SetFlutterViewController(UIViewController* flutter_view_controller);
UIViewController* getFlutterViewController();
void RegisterViewFactory(
NSObject<FlutterPlatformViewFactory>* factory,
NSString* factoryId,
@ -255,6 +243,8 @@ class FlutterPlatformViewsController {
std::map<std::string, FlutterPlatformViewGestureRecognizersBlockingPolicy>
gesture_recognizers_blocking_policies;
std::unique_ptr<fml::WeakPtrFactory<FlutterPlatformViewsController>> weak_factory_;
void OnCreate(FlutterMethodCall* call, FlutterResult& result);
void OnDispose(FlutterMethodCall* call, FlutterResult& result);
void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result);
@ -313,4 +303,23 @@ class FlutterPlatformViewsController {
} // namespace flutter
// A UIView that is used as the parent for embedded UIViews.
//
// This view has 2 roles:
// 1. Delay or prevent touch events from arriving the embedded view.
// 2. Dispatching all events that are hittested to the embedded view to the FlutterView.
@interface FlutterTouchInterceptingView : UIView
- (instancetype)initWithEmbeddedView:(UIView*)embeddedView
platformViewsController:
(fml::WeakPtr<flutter::FlutterPlatformViewsController>)platformViewsController
gestureRecognizersBlockingPolicy:
(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy;
// Stop delaying any active touch sequence (and let it arrive the embedded view).
- (void)releaseGesture;
// Prevent the touch sequence from ever arriving to the embedded view.
- (void)blockGesture;
@end
#endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMVIEWS_INTERNAL_H_

View File

@ -24,10 +24,15 @@ FlutterPlatformViewLayer::FlutterPlatformViewLayer(
FlutterPlatformViewLayer::~FlutterPlatformViewLayer() = default;
FlutterPlatformViewsController::FlutterPlatformViewsController()
: layer_pool_(std::make_unique<FlutterPlatformViewLayerPool>()){};
: layer_pool_(std::make_unique<FlutterPlatformViewLayerPool>()),
weak_factory_(std::make_unique<fml::WeakPtrFactory<FlutterPlatformViewsController>>(this)){};
FlutterPlatformViewsController::~FlutterPlatformViewsController() = default;
fml::WeakPtr<flutter::FlutterPlatformViewsController> FlutterPlatformViewsController::GetWeakPtr() {
return weak_factory_->GetWeakPtr();
}
CATransform3D GetCATransform3DFromSkMatrix(const SkMatrix& matrix) {
// Skia only supports 2D transform so we don't map z.
CATransform3D transform = CATransform3DIdentity;