Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ @implementation RNBetterTapGestureRecognizer {
NSUInteger _tapsSoFar;
CGPoint _initPosition;
NSInteger _maxNumberOfTouches;
// Pending `cancel` invocations scheduled via dispatch_after. We use dispatch blocks instead of
// performSelector:afterDelay: because the latter schedules its timer in NSDefaultRunLoopMode only,
// which means it is starved while a sibling UIScrollView keeps the run loop in UITrackingRunLoopMode
// (during a drag or momentum deceleration). dispatch_after fires regardless of run loop mode, so the
// tap can still fail/finalize on time while a list is scrolling. See issue #3471.
NSMutableArray<dispatch_block_t> *_pendingCancellations;
}

static const NSUInteger defaultNumberOfTaps = 1;
Expand All @@ -57,10 +63,33 @@ - (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
_maxDeltaX = NAN;
_maxDeltaY = NAN;
_maxDistSq = NAN;
_pendingCancellations = [NSMutableArray array];
}
return self;
}

- (void)scheduleCancelAfterDelay:(NSTimeInterval)delay
{
__weak typeof(self) weakSelf = self;

dispatch_block_t block = dispatch_block_create(0, ^{
[weakSelf cancel];
});

[_pendingCancellations addObject:block];

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), block);
}

- (void)cancelPendingCancellations
{
for (dispatch_block_t block in _pendingCancellations) {
dispatch_block_cancel(block);
}

[_pendingCancellations removeAllObjects];
}

- (void)triggerAction
{
[_gestureHandler handleGesture:self fromReset:NO fromManualStateChange:NO];
Expand Down Expand Up @@ -98,14 +127,14 @@ - (void)interactionsBegan:(NSSet *)touches withEvent:(UIEvent *)event
}
_tapsSoFar++;
if (_tapsSoFar) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
[self cancelPendingCancellations];
}
NSInteger numberOfTouches = [touches count];
if (numberOfTouches > _maxNumberOfTouches) {
_maxNumberOfTouches = numberOfTouches;
}
if (!isnan(_maxDuration)) {
[self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDuration];
[self scheduleCancelAfterDelay:_maxDuration];
}
self.state = UIGestureRecognizerStatePossible;
[self triggerAction];
Expand Down Expand Up @@ -137,10 +166,12 @@ - (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];

[self cancelPendingCancellations];

if (_numberOfTaps == _tapsSoFar && _maxNumberOfTouches >= _minPointers) {
self.state = UIGestureRecognizerStateEnded;
} else {
[self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDelay];
[self scheduleCancelAfterDelay:_maxDelay];
}
Comment thread
m-bert marked this conversation as resolved.
}

Expand Down Expand Up @@ -255,7 +286,7 @@ - (void)reset

[_gestureHandler.pointerTracker reset];

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
[self cancelPendingCancellations];
_tapsSoFar = 0;
_maxNumberOfTouches = 0;
self.enabled = _gestureHandler.enabled;
Expand Down
Loading