From 245317a621b18696c6fb8ed863c03ab5e9ffadc0 Mon Sep 17 00:00:00 2001 From: Amir Hardon Date: Thu, 27 Dec 2018 13:23:36 -0800 Subject: [PATCH] =?UTF-8?q?=20Reland=20"Only=20reject=20gestures=20to=20em?= =?UTF-8?q?bedded=20UIViews=20when=20the=20framework=20sa=E2=80=A6=20(#731?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This re-lands commit cc9c670, with a few fixes: - Keep the DelayingGestureRecognizer a discrete gesture recognizer, when it was set to a began state embedded WkWebViews wasn't receiving touch events. - Fix a bug of not retaining the forwardRecognizer pointer when assigning it to a scoped_nsobject. --- .../framework/Source/FlutterPlatformViews.mm | 66 +++++++++++++------ .../Source/FlutterPlatformViews_Internal.h | 4 ++ 2 files changed, 50 insertions(+), 20 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 6ca2f334dd..dcf970b377 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -29,6 +29,8 @@ void FlutterPlatformViewsController::OnMethodCall(FlutterMethodCall* call, Flutt OnDispose(call, result); } else if ([[call method] isEqualToString:@"acceptGesture"]) { OnAcceptGesture(call, result); + } else if ([[call method] isEqualToString:@"rejectGesture"]) { + OnRejectGesture(call, result); } else { result(FlutterMethodNotImplemented); } @@ -125,6 +127,24 @@ void FlutterPlatformViewsController::OnAcceptGesture(FlutterMethodCall* call, result(nil); } +void FlutterPlatformViewsController::OnRejectGesture(FlutterMethodCall* call, + FlutterResult& result) { + NSDictionary* args = [call arguments]; + int64_t viewId = [args[@"id"] longLongValue]; + + if (views_.count(viewId) == 0) { + result([FlutterError errorWithCode:@"unknown_view" + message:@"trying to set gesture state for an unknown view" + details:[NSString stringWithFormat:@"view id: '%lld'", viewId]]); + return; + } + + FlutterTouchInterceptingView* view = touch_interceptors_[viewId].get(); + [view blockGesture]; + + result(nil); +} + void FlutterPlatformViewsController::RegisterViewFactory( NSObject* factory, NSString* factoryId) { @@ -269,6 +289,9 @@ void FlutterPlatformViewsController::EnsureGLOverlayInitialized( // invoking an acceptGesture method on the platform_views channel). And this is how we allow the // Flutter framework to delay or prevent the embedded view from getting a touch sequence. @interface DelayingGestureRecognizer : UIGestureRecognizer +- (instancetype)initWithTarget:(id)target + action:(SEL)action + forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer; @end // While the DelayingGestureRecognizer is preventing touches from hitting the responder chain @@ -301,7 +324,10 @@ void FlutterPlatformViewsController::EnsureGLOverlayInitialized( [[[ForwardingGestureRecognizer alloc] initWithTarget:self flutterView:flutterView] autorelease]; - _delayingRecognizer.reset([[DelayingGestureRecognizer alloc] initWithTarget:self action:nil]); + _delayingRecognizer.reset([[DelayingGestureRecognizer alloc] + initWithTarget:self + action:nil + forwardingRecognizer:forwardingRecognizer]); [self addGestureRecognizer:_delayingRecognizer.get()]; [self addGestureRecognizer:forwardingRecognizer]; @@ -312,21 +338,34 @@ void FlutterPlatformViewsController::EnsureGLOverlayInitialized( - (void)releaseGesture { _delayingRecognizer.get().state = UIGestureRecognizerStateFailed; } + +- (void)blockGesture { + _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; +} + @end -@implementation DelayingGestureRecognizer -- (instancetype)initWithTarget:(id)target action:(SEL)action { +@implementation DelayingGestureRecognizer { + fml::scoped_nsobject _forwardingRecognizer; +} + +- (instancetype)initWithTarget:(id)target + action:(SEL)action + forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer { self = [super initWithTarget:target action:action]; if (self) { self.delaysTouchesBegan = YES; self.delegate = self; + _forwardingRecognizer.reset([forwardingRecognizer retain]); } return self; } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer { - return otherGestureRecognizer != self; + // The forwarding gesture recognizer should always get all touch events, so it should not be + // required to fail by any other gesture recognizer. + return otherGestureRecognizer != _forwardingRecognizer.get() && otherGestureRecognizer != self; } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer @@ -334,21 +373,8 @@ void FlutterPlatformViewsController::EnsureGLOverlayInitialized( return otherGestureRecognizer == self; } -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - // The gesture has ended, and the delaying gesture recognizer was not failed, we recognize - // the gesture to prevent the touches from being dispatched to the embedded view. - // - // This doesn't work well with gestures that are recognized by the Flutter framework after - // all pointers are up. - // - // TODO(amirh): explore if we can instead set this to recognized when the next touch sequence - // begins, or we can use a framework signal for restarting the recognizers (e.g when the - // gesture arena is resolved). - self.state = UIGestureRecognizerStateRecognized; -} - - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - self.state = UIGestureRecognizerStateRecognized; + self.state = UIGestureRecognizerStateFailed; } @end @@ -380,12 +406,12 @@ void FlutterPlatformViewsController::EnsureGLOverlayInitialized( - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { [_flutterView touchesEnded:touches withEvent:event]; - self.state = UIGestureRecognizerStateRecognized; + self.state = UIGestureRecognizerStateEnded; } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { [_flutterView touchesCancelled:touches withEvent:event]; - self.state = UIGestureRecognizerStateRecognized; + self.state = UIGestureRecognizerStateFailed; } - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index 4173a1ebf6..7c980896bf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -22,6 +22,9 @@ // 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 namespace shell { @@ -89,6 +92,7 @@ class FlutterPlatformViewsController { void OnCreate(FlutterMethodCall* call, FlutterResult& result); void OnDispose(FlutterMethodCall* call, FlutterResult& result); void OnAcceptGesture(FlutterMethodCall* call, FlutterResult& result); + void OnRejectGesture(FlutterMethodCall* call, FlutterResult& result); void EnsureOverlayInitialized(int64_t overlay_id); void EnsureGLOverlayInitialized(int64_t overlay_id, -- GitLab