1#import "RNFlingHandler.h" 2 3@interface RNBetterSwipeGestureRecognizer : UISwipeGestureRecognizer 4 5- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler; 6 7@end 8 9@implementation RNBetterSwipeGestureRecognizer { 10 __weak RNGestureHandler *_gestureHandler; 11 CGPoint _lastPoint; // location of the most recently updated touch, relative to the view 12 bool _hasBegan; // whether the `BEGAN` event has been sent 13} 14 15- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler 16{ 17 if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) { 18 _gestureHandler = gestureHandler; 19 _lastPoint = CGPointZero; 20 _hasBegan = NO; 21 } 22 return self; 23} 24 25- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 26{ 27 _lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view]; 28 [_gestureHandler reset]; 29 [super touchesBegan:touches withEvent:event]; 30 [_gestureHandler.pointerTracker touchesBegan:touches withEvent:event]; 31 32 // self.numberOfTouches doesn't work for this because in case than one finger is required, 33 // when holding one finger on the screen and tapping with the second one, numberOfTouches is equal 34 // to 2 only for the first tap but 1 for all the following ones 35 if (!_hasBegan) { 36 [self triggerAction]; 37 _hasBegan = YES; 38 } 39} 40 41- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 42{ 43 _lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view]; 44 [super touchesMoved:touches withEvent:event]; 45 [_gestureHandler.pointerTracker touchesMoved:touches withEvent:event]; 46} 47 48- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 49{ 50 _lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view]; 51 [super touchesEnded:touches withEvent:event]; 52 [_gestureHandler.pointerTracker touchesEnded:touches withEvent:event]; 53} 54 55- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 56{ 57 _lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view]; 58 [super touchesCancelled:touches withEvent:event]; 59 [_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event]; 60} 61 62- (void)triggerAction 63{ 64 [_gestureHandler handleGesture:self]; 65} 66 67- (void)reset 68{ 69 [self triggerAction]; 70 [_gestureHandler.pointerTracker reset]; 71 _hasBegan = NO; 72 [super reset]; 73} 74 75- (CGPoint)getLastLocation 76{ 77 // I think keeping the location of only one touch is enough since it would be used to determine the direction 78 // of the movement, and if it's wrong the recognizer fails anyway. 79 // In case the location of all touches is required, touch events are the way to go 80 return _lastPoint; 81} 82 83@end 84 85@implementation RNFlingGestureHandler 86 87- (instancetype)initWithTag:(NSNumber *)tag 88{ 89 if ((self = [super initWithTag:tag])) { 90 _recognizer = [[RNBetterSwipeGestureRecognizer alloc] initWithGestureHandler:self]; 91 } 92 return self; 93} 94- (void)resetConfig 95{ 96 [super resetConfig]; 97 UISwipeGestureRecognizer *recognizer = (UISwipeGestureRecognizer *)_recognizer; 98 recognizer.direction = UISwipeGestureRecognizerDirectionRight; 99#if !TARGET_OS_TV 100 recognizer.numberOfTouchesRequired = 1; 101#endif 102} 103 104- (void)configure:(NSDictionary *)config 105{ 106 [super configure:config]; 107 UISwipeGestureRecognizer *recognizer = (UISwipeGestureRecognizer *)_recognizer; 108 109 id prop = config[@"direction"]; 110 if (prop != nil) { 111 recognizer.direction = [RCTConvert NSInteger:prop]; 112 } 113 114#if !TARGET_OS_TV 115 prop = config[@"numberOfPointers"]; 116 if (prop != nil) { 117 recognizer.numberOfTouchesRequired = [RCTConvert NSInteger:prop]; 118 } 119#endif 120} 121 122- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 123{ 124 RNGestureHandlerState savedState = _lastState; 125 BOOL shouldBegin = [super gestureRecognizerShouldBegin:gestureRecognizer]; 126 _lastState = savedState; 127 128 return shouldBegin; 129} 130 131- (RNGestureHandlerEventExtraData *)eventExtraData:(id)_recognizer 132{ 133 // For some weird reason [recognizer locationInView:recognizer.view.window] returns (0, 0). 134 // To calculate the correct absolute position, first calculate the absolute position of the 135 // view inside the root view controller (https://stackoverflow.com/a/7448573) and then 136 // add the relative touch position to it. 137 138 RNBetterSwipeGestureRecognizer *recognizer = (RNBetterSwipeGestureRecognizer *)_recognizer; 139 140 CGPoint viewAbsolutePosition = 141 [recognizer.view convertPoint:recognizer.view.bounds.origin 142 toView:[UIApplication sharedApplication].keyWindow.rootViewController.view]; 143 CGPoint locationInView = [recognizer getLastLocation]; 144 145 return [RNGestureHandlerEventExtraData 146 forPosition:locationInView 147 withAbsolutePosition:CGPointMake( 148 viewAbsolutePosition.x + locationInView.x, viewAbsolutePosition.y + locationInView.y) 149 withNumberOfTouches:recognizer.numberOfTouches]; 150} 151 152@end 153