From 5e5dbebb271f6c6e9bc55626de87e5dad4b2ef5d Mon Sep 17 00:00:00 2001 From: osdnk Date: Wed, 27 Feb 2019 12:48:06 +0100 Subject: [PATCH] Memoize tap GH state for sending proper value after releasing finger --- ios/Handlers/RNTapHandler.m | 49 +++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/ios/Handlers/RNTapHandler.m b/ios/Handlers/RNTapHandler.m index 37bd04b20e..33db3d16a6 100644 --- a/ios/Handlers/RNTapHandler.m +++ b/ios/Handlers/RNTapHandler.m @@ -12,8 +12,8 @@ #import -// RNBetterTapGestureRecognizer extends UIGestureRecognizer instead of UITapGestureRecognizer -// because the latter does not allow for parameters like maxDelay, maxDuration, minPointers, +// RNBetterTapGestureRecognizer extends UIGestureRecognizer instead of UITapGestureRecognizer +// because the latter does not allow for parameters like maxDelay, maxDuration, minPointers, // maxDelta to be configured. Using our custom implementation of tap recognizer we are able // to support these. @@ -26,6 +26,16 @@ @interface RNBetterTapGestureRecognizer : UIGestureRecognizer @property (nonatomic) CGFloat maxDeltaX; @property (nonatomic) CGFloat maxDeltaY; @property (nonatomic) NSInteger minPointers; +// Memoizing positions and number of pointers are necessary +// since it might happen that changing state of recognizer could +// be delayed in order to wait for failure of another gesture recognition. +// Then data might be requested after raising finger which will +// lead to filling them with zeros if they are not memoized. +// This issue is only reasonable with continuous handlers and is +// handled properly with with implementation based on UIKit's recognizer +@property (nonatomic) CGPoint memoizedAbsolutePosition; +@property (nonatomic) CGPoint memoizedPosition; +@property (nonatomic) NSInteger memoizedNumberOfPointers; - (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler; @@ -92,18 +102,18 @@ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event if (numberOfTouches > _maxNumberOfTouches) { _maxNumberOfTouches = numberOfTouches; } - + if (self.state != UIGestureRecognizerStatePossible) { return; } - + if ([self shouldFailUnderCustomCriteria]) { self.state = UIGestureRecognizerStateFailed; [self triggerAction]; [self reset]; return; } - + self.state = UIGestureRecognizerStatePossible; [self triggerAction]; } @@ -120,7 +130,7 @@ - (BOOL)shouldFailUnderCustomCriteria return YES; } } - + CGPoint trans = [self translationInView]; if (TEST_MAX_IF_NOT_NAN(fabs(trans.x), _maxDeltaX)) { return YES; @@ -138,6 +148,9 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; if (_numberOfTaps == _tapsSoFar && _maxNumberOfTouches >= _minPointers) { + _memoizedPosition = [self locationInView:self.view]; + _memoizedAbsolutePosition = [self locationInView:self.view.window]; + _memoizedNumberOfPointers = self.numberOfTouches; self.state = UIGestureRecognizerStateEnded; [self reset]; } else { @@ -180,22 +193,22 @@ - (void)configure:(NSDictionary *)config { [super configure:config]; RNBetterTapGestureRecognizer *recognizer = (RNBetterTapGestureRecognizer *)_recognizer; - + APPLY_INT_PROP(numberOfTaps); APPLY_INT_PROP(minPointers); APPLY_FLOAT_PROP(maxDeltaX); APPLY_FLOAT_PROP(maxDeltaY); - + id prop = config[@"maxDelayMs"]; if (prop != nil) { recognizer.maxDelay = [RCTConvert CGFloat:prop] / 1000.0; } - + prop = config[@"maxDurationMs"]; if (prop != nil) { recognizer.maxDuration = [RCTConvert CGFloat:prop] / 1000.0; } - + prop = config[@"maxDist"]; if (prop != nil) { CGFloat dist = [RCTConvert CGFloat:prop]; @@ -203,5 +216,21 @@ - (void)configure:(NSDictionary *)config } } +- (RNGestureHandlerEventExtraData *)eventExtraData:(RNBetterTapGestureRecognizer *)recognizer +{ + if (recognizer.numberOfTouches == 0) { + // this condition is fulfilled if pointer is raised and it happens + // if is waiting for another one. + return [RNGestureHandlerEventExtraData + forPosition:recognizer.memoizedPosition + withAbsolutePosition:recognizer.memoizedAbsolutePosition + withNumberOfTouches:recognizer.memoizedNumberOfPointers]; + } + return [RNGestureHandlerEventExtraData + forPosition:[recognizer locationInView:recognizer.view] + withAbsolutePosition:[recognizer locationInView:recognizer.view.window] + withNumberOfTouches:recognizer.numberOfTouches]; +} + @end