Permalink
Browse files

[ReactNative] Delay sending touches to JS until dependent (native) ge…

…sture recognizers fail

Summary:
Previously, JS touches were being interpreted simultaneously with native gesture recognizers.
  • Loading branch information...
sophiebits committed Aug 7, 2015
1 parent 205a2c0 commit 2d66e10ec45b9fbbf005d7d110f57c22cf972338
Showing with 44 additions and 22 deletions.
  1. +44 −22 React/Base/RCTTouchHandler.m
@@ -23,6 +23,7 @@
@implementation RCTTouchHandler
{
__weak RCTBridge *_bridge;
BOOL _dispatchedInitialTouches;
/**
* Arrays managed in parallel tracking native touch object along with the
@@ -42,9 +43,10 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
{
RCTAssertParam(bridge);
if ((self = [super initWithTarget:nil action:NULL])) {
if ((self = [super initWithTarget:self action:@selector(handleGestureUpdate:)])) {
_bridge = bridge;
_dispatchedInitialTouches = NO;
_nativeTouches = [[NSMutableOrderedSet alloc] init];
_reactTouches = [[NSMutableArray alloc] init];
_touchViews = [[NSMutableArray alloc] init];
@@ -216,56 +218,72 @@ static BOOL RCTAnyTouchesChanged(NSSet *touches)
return NO;
}
- (void)handleGestureUpdate:(UIGestureRecognizer *)gesture {
// If the gesture just recognized, send all the touches over to JS as if they just began
if (self.state == UIGestureRecognizerStateBegan) {
[self _updateAndDispatchTouches:[_nativeTouches set] eventName:@"topTouchStart" originatingTime:0];
// We store this flag separately from `state` because after a gesture is recognized, its `state` changes immediately but its action (that is, this method) isn't fired until dependent gesture recognizers have failed. We only want to send move/end/cancel touch updates if we've sent the touchStart events.
_dispatchedInitialTouches = YES;
}
// For the other states, we could dispatch the updates here but since we specifically send info about which touches changed, it's simpler to dispatch the updates from the raw touch methods below.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
// "start" has to record new touches before extracting the event.
// "end"/"cancel" needs to remove the touch *after* extracting the event.
[self _recordNewTouches:touches];
[self _updateAndDispatchTouches:touches eventName:@"topTouchStart" originatingTime:event.timestamp];
self.state = UIGestureRecognizerStateBegan;
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchStart" originatingTime:event.timestamp];
self.state = UIGestureRecognizerStateChanged;
} else {
self.state = UIGestureRecognizerStateBegan;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStateFailed) {
return;
}
[self _updateAndDispatchTouches:touches eventName:@"topTouchMove" originatingTime:event.timestamp];
if (self.state == UIGestureRecognizerStateBegan) {
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchMove" originatingTime:event.timestamp];
self.state = UIGestureRecognizerStateChanged;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd" originatingTime:event.timestamp];
[self _recordRemovedTouches:touches];
if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateEnded;
} else if (RCTAnyTouchesChanged(event.allTouches)) {
self.state = UIGestureRecognizerStateChanged;
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd" originatingTime:event.timestamp];
if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateEnded;
} else if (RCTAnyTouchesChanged(event.allTouches)) {
self.state = UIGestureRecognizerStateChanged;
}
}
[self _recordRemovedTouches:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel" originatingTime:event.timestamp];
[self _recordRemovedTouches:touches];
if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateCancelled;
} else if (RCTAnyTouchesChanged(event.allTouches)) {
self.state = UIGestureRecognizerStateChanged;
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel" originatingTime:event.timestamp];
if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateCancelled;
} else if (RCTAnyTouchesChanged(event.allTouches)) {
self.state = UIGestureRecognizerStateChanged;
}
}
[self _recordRemovedTouches:touches];
}
- (BOOL)canPreventGestureRecognizer:(__unused UIGestureRecognizer *)preventedGestureRecognizer
@@ -278,4 +296,8 @@ - (BOOL)canBePreventedByGestureRecognizer:(__unused UIGestureRecognizer *)preven
return NO;
}
- (void)reset {
_dispatchedInitialTouches = NO;
}
@end

0 comments on commit 2d66e10

Please sign in to comment.