From c14cc123d5be04f109ba2209a3393ee2ffa84149 Mon Sep 17 00:00:00 2001 From: Martin Kralik Date: Thu, 21 Jan 2016 13:43:58 -0800 Subject: [PATCH] API for cancelling RCTTouchHandler Reviewed By: spicyj Differential Revision: D2846573 fb-gh-sync-id: 652864c773d03c24f0d68dd8335401eb052fc1bf --- React/Base/RCTRootView.h | 19 +++++++++++++++++++ React/Base/RCTRootView.m | 11 +++++++++-- React/Base/RCTTouchHandler.h | 1 + React/Base/RCTTouchHandler.m | 6 ++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/React/Base/RCTRootView.h b/React/Base/RCTRootView.h index b11881de9..ba1fe8712 100644 --- a/React/Base/RCTRootView.h +++ b/React/Base/RCTRootView.h @@ -123,6 +123,25 @@ extern NSString *const RCTContentDidAppearNotification; */ @property (nonatomic, strong) UIView *loadingView; +/** + * Calling this will result in emitting a "touches cancelled" event to js, + * which effectively cancels all js "gesture recognizers" such as as touchable + * (unless they explicitely ignore cancellation events, but noone should do that). + * + * This API is exposed for integration purposes where you embed RN rootView + * in a native view with a native gesture recognizer, + * whose activation should prevent any in-flight js "gesture recognizer" from activating. + * + * An example would be RN rootView embedded in an UIScrollView. + * When you touch down on a touchable component and drag your finger up, + * you don't want any touch to be registered as soon as the UIScrollView starts scrolling. + * + * Note that this doesn't help with tapping on a touchable element that is being scrolled, + * unless you can call cancelTouches exactly between "touches began" and "touches ended" events. + * This is a reason why this API may be soon removed in favor of a better solution. + */ +- (void)cancelTouches; + /** * Timings for hiding the loading view after the content has loaded. Both of * these values default to 0.25 seconds. diff --git a/React/Base/RCTRootView.m b/React/Base/RCTRootView.m index f53c278b3..c0481e076 100644 --- a/React/Base/RCTRootView.m +++ b/React/Base/RCTRootView.m @@ -38,6 +38,8 @@ NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotificat @property (nonatomic, readonly) BOOL contentHasAppeared; +@property (nonatomic, readonly, strong) RCTTouchHandler *touchHandler; + - (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; @end @@ -257,6 +259,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) [_contentView invalidate]; } +- (void)cancelTouches +{ + [[_contentView touchHandler] cancel]; +} + @end @implementation RCTUIManager (RCTRootView) @@ -273,7 +280,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) @implementation RCTRootContentView { __weak RCTBridge *_bridge; - RCTTouchHandler *_touchHandler; UIColor *_backgroundColor; } @@ -335,7 +341,8 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder) * the react tag is assigned every time we load new content. */ self.reactTag = [_bridge.uiManager allocateRootTag]; - [self addGestureRecognizer:[[RCTTouchHandler alloc] initWithBridge:_bridge]]; + _touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge]; + [self addGestureRecognizer:_touchHandler]; [_bridge.uiManager registerRootView:self]; } diff --git a/React/Base/RCTTouchHandler.h b/React/Base/RCTTouchHandler.h index 9d3f1cbd4..5ea191993 100644 --- a/React/Base/RCTTouchHandler.h +++ b/React/Base/RCTTouchHandler.h @@ -16,5 +16,6 @@ @interface RCTTouchHandler : UIGestureRecognizer - (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; +- (void)cancel; @end diff --git a/React/Base/RCTTouchHandler.m b/React/Base/RCTTouchHandler.m index 707830aab..f2490a7ad 100644 --- a/React/Base/RCTTouchHandler.m +++ b/React/Base/RCTTouchHandler.m @@ -311,4 +311,10 @@ static BOOL RCTAnyTouchesChanged(NSSet *touches) _dispatchedInitialTouches = NO; } +- (void)cancel +{ + self.enabled = NO; + self.enabled = YES; +} + @end