feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,4 @@
#import "RNGestureHandler.h"
@interface RNFlingGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,173 @@
#import "RNFlingHandler.h"
#if !TARGET_OS_OSX
@interface RNBetterSwipeGestureRecognizer : UISwipeGestureRecognizer
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNBetterSwipeGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
CGPoint _lastPoint; // location of the most recently updated touch, relative to the view
bool _hasBegan; // whether the `BEGAN` event has been sent
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_lastPoint = CGPointZero;
_hasBegan = NO;
}
return self;
}
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
_lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view];
[_gestureHandler reset];
[super touchesBegan:touches withEvent:event];
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
// self.numberOfTouches doesn't work for this because in case than one finger is required,
// when holding one finger on the screen and tapping with the second one, numberOfTouches is equal
// to 2 only for the first tap but 1 for all the following ones
if (!_hasBegan) {
[self triggerAction];
_hasBegan = YES;
}
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
_lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view];
[super touchesMoved:touches withEvent:event];
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
_lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view];
[super touchesEnded:touches withEvent:event];
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
_lastPoint = [[[touches allObjects] objectAtIndex:0] locationInView:_gestureHandler.recognizer.view];
[super touchesCancelled:touches withEvent:event];
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
- (void)triggerAction
{
[_gestureHandler handleGesture:self];
}
- (void)reset
{
[self triggerAction];
[_gestureHandler.pointerTracker reset];
_hasBegan = NO;
[super reset];
[_gestureHandler reset];
}
- (CGPoint)getLastLocation
{
// I think keeping the location of only one touch is enough since it would be used to determine the direction
// of the movement, and if it's wrong the recognizer fails anyway.
// In case the location of all touches is required, touch events are the way to go
return _lastPoint;
}
@end
@implementation RNFlingGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNBetterSwipeGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (void)resetConfig
{
[super resetConfig];
UISwipeGestureRecognizer *recognizer = (UISwipeGestureRecognizer *)_recognizer;
recognizer.direction = UISwipeGestureRecognizerDirectionRight;
#if !TARGET_OS_TV
recognizer.numberOfTouchesRequired = 1;
#endif
}
- (void)configure:(NSDictionary *)config
{
[super configure:config];
UISwipeGestureRecognizer *recognizer = (UISwipeGestureRecognizer *)_recognizer;
id prop = config[@"direction"];
if (prop != nil) {
recognizer.direction = [RCTConvert NSInteger:prop];
}
#if !TARGET_OS_TV
prop = config[@"numberOfPointers"];
if (prop != nil) {
recognizer.numberOfTouchesRequired = [RCTConvert NSInteger:prop];
}
#endif
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
RNGestureHandlerState savedState = _lastState;
BOOL shouldBegin = [super gestureRecognizerShouldBegin:gestureRecognizer];
_lastState = savedState;
return shouldBegin;
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(id)_recognizer
{
// For some weird reason [recognizer locationInView:recognizer.view.window] returns (0, 0).
// To calculate the correct absolute position, first calculate the absolute position of the
// view inside the root view controller (https://stackoverflow.com/a/7448573) and then
// add the relative touch position to it.
RNBetterSwipeGestureRecognizer *recognizer = (RNBetterSwipeGestureRecognizer *)_recognizer;
CGPoint viewAbsolutePosition = [recognizer.view convertPoint:recognizer.view.bounds.origin
toView:RCTKeyWindow().rootViewController.view];
CGPoint locationInView = [recognizer getLastLocation];
return [RNGestureHandlerEventExtraData
forPosition:locationInView
withAbsolutePosition:CGPointMake(
viewAbsolutePosition.x + locationInView.x, viewAbsolutePosition.y + locationInView.y)
withNumberOfTouches:recognizer.numberOfTouches
withPointerType:_pointerType];
}
@end
#else
@implementation RNFlingGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
RCTLogWarn(@"FlingGestureHandler is not supported on macOS");
if ((self = [super initWithTag:tag])) {
_recognizer = [NSGestureRecognizer alloc];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,4 @@
#import "RNGestureHandler.h"
@interface RNForceTouchHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,195 @@
#import "RNForceTouchHandler.h"
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <React/RCTConvert.h>
@interface RNForceTouchGestureRecognizer : UIGestureRecognizer
@property (nonatomic) CGFloat maxForce;
@property (nonatomic) CGFloat minForce;
@property (nonatomic) CGFloat force;
@property (nonatomic) BOOL feedbackOnActivation;
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNForceTouchGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
RNGHUITouch *_firstTouch;
}
static const CGFloat defaultForce = 0;
static const CGFloat defaultMinForce = 0.2;
static const CGFloat defaultMaxForce = NAN;
static const BOOL defaultFeedbackOnActivation = NO;
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_force = defaultForce;
_minForce = defaultMinForce;
_maxForce = defaultMaxForce;
_feedbackOnActivation = defaultFeedbackOnActivation;
}
return self;
}
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
if (_firstTouch) {
// ignore rest of fingers
return;
}
[super touchesBegan:touches withEvent:event];
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
_firstTouch = [touches anyObject];
[self handleForceWithTouches:touches];
self.state = UIGestureRecognizerStatePossible;
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
if (![touches containsObject:_firstTouch]) {
// Considered only the very first touch
return;
}
[super touchesMoved:touches withEvent:event];
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
[self handleForceWithTouches:touches];
if ([self shouldFail]) {
self.state = UIGestureRecognizerStateFailed;
return;
}
if (self.state == UIGestureRecognizerStatePossible && [self shouldActivate]) {
[self performFeedbackIfRequired];
self.state = UIGestureRecognizerStateBegan;
}
}
- (BOOL)shouldActivate
{
return (_force >= _minForce);
}
- (BOOL)shouldFail
{
return TEST_MAX_IF_NOT_NAN(_force, _maxForce);
}
- (void)performFeedbackIfRequired
{
#if !TARGET_OS_TV && !TARGET_OS_VISION
if (_feedbackOnActivation) {
if (@available(iOS 10.0, *)) {
[[[UIImpactFeedbackGenerator alloc] initWithStyle:(UIImpactFeedbackStyleMedium)] impactOccurred];
}
}
#endif
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
if (![touches containsObject:_firstTouch]) {
// Considered only the very first touch
return;
}
[super touchesEnded:touches withEvent:event];
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
if (self.state == UIGestureRecognizerStateBegan || self.state == UIGestureRecognizerStateChanged) {
self.state = UIGestureRecognizerStateEnded;
} else {
self.state = UIGestureRecognizerStateFailed;
}
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
- (void)handleForceWithTouches:(NSSet<RNGHUITouch *> *)touches
{
_force = _firstTouch.force / _firstTouch.maximumPossibleForce;
}
- (void)reset
{
[_gestureHandler.pointerTracker reset];
[super reset];
[_gestureHandler reset];
_force = 0;
_firstTouch = NULL;
}
@end
@implementation RNForceTouchHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNForceTouchGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (void)resetConfig
{
[super resetConfig];
RNForceTouchGestureRecognizer *recognizer = (RNForceTouchGestureRecognizer *)_recognizer;
recognizer.feedbackOnActivation = defaultFeedbackOnActivation;
recognizer.maxForce = defaultMaxForce;
recognizer.minForce = defaultMinForce;
}
- (void)configure:(NSDictionary *)config
{
[super configure:config];
RNForceTouchGestureRecognizer *recognizer = (RNForceTouchGestureRecognizer *)_recognizer;
APPLY_FLOAT_PROP(maxForce);
APPLY_FLOAT_PROP(minForce);
id prop = config[@"feedbackOnActivation"];
if (prop != nil) {
recognizer.feedbackOnActivation = [RCTConvert BOOL:prop];
}
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(RNForceTouchGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forForce:recognizer.force
forPosition:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withNumberOfTouches:recognizer.numberOfTouches
withPointerType:_pointerType];
}
@end
#else
@implementation RNForceTouchHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [NSGestureRecognizer alloc];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,12 @@
//
// RNHoverHandler.h
// RNGestureHandler
//
// Created by Jakub Piasecki on 31/03/2023.
//
#import "RNGestureHandler.h"
API_AVAILABLE(ios(13.4))
@interface RNHoverGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,179 @@
//
// RNHoverHandler.m
// RNGestureHandler
//
// Created by Jakub Piasecki on 31/03/2023.
//
#import "RNHoverHandler.h"
#if !TARGET_OS_OSX
#import <React/RCTConvert.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#define CHECK_TARGET(__VERSION__) \
defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_##__VERSION__) && \
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_##__VERSION__ && !TARGET_OS_TV
typedef NS_ENUM(NSInteger, RNGestureHandlerHoverEffect) {
RNGestureHandlerHoverEffectNone = 0,
RNGestureHandlerHoverEffectLift,
RNGestureHandlerHoverEffectHightlight,
};
#if CHECK_TARGET(13_4)
API_AVAILABLE(ios(13.4))
@interface RNBetterHoverGestureRecognizer : UIHoverGestureRecognizer <UIPointerInteractionDelegate>
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@property (nonatomic) RNGestureHandlerHoverEffect hoverEffect;
@end
@implementation RNBetterHoverGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_hoverEffect = RNGestureHandlerHoverEffectNone;
}
return self;
}
- (void)triggerAction
{
[_gestureHandler handleGesture:self];
}
- (void)cancel
{
self.enabled = NO;
}
- (void)reset
{
[super reset];
[_gestureHandler reset];
}
- (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction styleForRegion:(UIPointerRegion *)region
{
if (interaction.view != nil && _hoverEffect != RNGestureHandlerHoverEffectNone) {
UITargetedPreview *preview = [[UITargetedPreview alloc] initWithView:interaction.view];
UIPointerEffect *effect = nil;
if (_hoverEffect == RNGestureHandlerHoverEffectLift) {
effect = [UIPointerLiftEffect effectWithPreview:preview];
} else if (_hoverEffect == RNGestureHandlerHoverEffectHightlight) {
effect = [UIPointerHoverEffect effectWithPreview:preview];
}
return [UIPointerStyle styleWithEffect:effect shape:nil];
}
return nil;
}
@end
#endif
@implementation RNHoverGestureHandler {
#if CHECK_TARGET(13_4)
UIPointerInteraction *_pointerInteraction;
#endif
}
- (instancetype)initWithTag:(NSNumber *)tag
{
#if TARGET_OS_TV
RCTLogWarn(@"HoverGestureHandler is not supported on tvOS");
#endif
if ((self = [super initWithTag:tag])) {
#if CHECK_TARGET(13_4)
if (@available(iOS 13.4, *)) {
_recognizer = [[RNBetterHoverGestureRecognizer alloc] initWithGestureHandler:self];
_pointerInteraction =
[[UIPointerInteraction alloc] initWithDelegate:(id<UIPointerInteractionDelegate>)_recognizer];
}
#endif
}
return self;
}
- (void)bindToView:(UIView *)view
{
#if CHECK_TARGET(13_4)
if (@available(iOS 13.4, *)) {
[super bindToView:view];
[view addInteraction:_pointerInteraction];
}
#endif
}
- (void)unbindFromView
{
#if CHECK_TARGET(13_4)
if (@available(iOS 13.4, *)) {
[super unbindFromView];
[self.recognizer.view removeInteraction:_pointerInteraction];
}
#endif
}
- (void)resetConfig
{
[super resetConfig];
#if CHECK_TARGET(13_4)
if (@available(iOS 13.4, *)) {
RNBetterHoverGestureRecognizer *recognizer = (RNBetterHoverGestureRecognizer *)_recognizer;
recognizer.hoverEffect = RNGestureHandlerHoverEffectNone;
}
#endif
}
- (void)configure:(NSDictionary *)config
{
[super configure:config];
#if CHECK_TARGET(13_4)
if (@available(iOS 13.4, *)) {
RNBetterHoverGestureRecognizer *recognizer = (RNBetterHoverGestureRecognizer *)_recognizer;
APPLY_INT_PROP(hoverEffect);
}
#endif
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forPosition:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withPointerType:UITouchTypePencil];
}
@end
#else
@implementation RNHoverGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
RCTLogWarn(@"HoverGestureHandler is not supported on macOS");
if ((self = [super initWithTag:tag])) {
_recognizer = [NSGestureRecognizer alloc];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,12 @@
//
// RNLongPressHandler.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNLongPressGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,197 @@
//
// RNLongPressHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNLongPressHandler.h"
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <React/RCTConvert.h>
@interface RNBetterLongPressGestureRecognizer : UILongPressGestureRecognizer {
CFTimeInterval startTime;
CFTimeInterval previousTime;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
- (void)handleGesture:(UIGestureRecognizer *)recognizer;
- (NSUInteger)getDuration;
@end
@implementation RNBetterLongPressGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
CGPoint _initPosition;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:self action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
}
return self;
}
- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
previousTime = CACurrentMediaTime();
[_gestureHandler handleGesture:recognizer];
}
- (void)triggerAction
{
[self handleGesture:self];
}
- (CGPoint)translationInView
{
CGPoint currentPosition = [self locationInView:self.view];
return CGPointMake(currentPosition.x - _initPosition.x, currentPosition.y - _initPosition.y);
}
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
[super touchesBegan:touches withEvent:event];
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
_initPosition = [self locationInView:self.view];
startTime = CACurrentMediaTime();
[_gestureHandler reset];
[self triggerAction];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
CGPoint trans = [self translationInView];
if ((_gestureHandler.shouldCancelWhenOutside && ![_gestureHandler containsPointInView]) ||
(TEST_MAX_IF_NOT_NAN(
fabs(trans.y * trans.y + trans.x + trans.x), self.allowableMovement * self.allowableMovement))) {
self.enabled = NO;
self.enabled = YES;
}
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
- (void)reset
{
if (self.state == UIGestureRecognizerStateFailed) {
[self triggerAction];
}
[_gestureHandler.pointerTracker reset];
[super reset];
[_gestureHandler reset];
}
- (NSUInteger)getDuration
{
return (previousTime - startTime) * 1000;
}
@end
@implementation RNLongPressGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNBetterLongPressGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (void)resetConfig
{
[super resetConfig];
UILongPressGestureRecognizer *recognizer = (UILongPressGestureRecognizer *)_recognizer;
recognizer.minimumPressDuration = 0.5;
recognizer.allowableMovement = 10;
}
- (void)configure:(NSDictionary *)config
{
[super configure:config];
UILongPressGestureRecognizer *recognizer = (UILongPressGestureRecognizer *)_recognizer;
id prop = config[@"minDurationMs"];
if (prop != nil) {
recognizer.minimumPressDuration = [RCTConvert CGFloat:prop] / 1000.0;
}
prop = config[@"maxDist"];
if (prop != nil) {
recognizer.allowableMovement = [RCTConvert CGFloat:prop];
}
}
- (RNGestureHandlerState)state
{
// For long press recognizer we treat "Began" state as "active"
// as it changes its state to "Began" as soon as the the minimum
// hold duration timeout is reached, whereas state "Changed" is
// only set after "Began" phase if there is some movement.
if (_recognizer.state == UIGestureRecognizerStateBegan) {
return RNGestureHandlerStateActive;
}
return [super state];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// same as TapGH, this needs to be unified when all handlers are updated
RNGestureHandlerState savedState = _lastState;
BOOL shouldBegin = [super gestureRecognizerShouldBegin:gestureRecognizer];
_lastState = savedState;
return shouldBegin;
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forPosition:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withNumberOfTouches:recognizer.numberOfTouches
withDuration:[(RNBetterLongPressGestureRecognizer *)recognizer getDuration]
withPointerType:_pointerType];
}
@end
#else
@implementation RNLongPressGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
RCTLogWarn(@"LongPressGestureHandler is not supported on macOS");
if ((self = [super initWithTag:tag])) {
_recognizer = [NSGestureRecognizer alloc];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,4 @@
#import "RNGestureHandler.h"
@interface RNManualGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,109 @@
#import "RNManualHandler.h"
#if !TARGET_OS_OSX
@interface RNManualRecognizer : UIGestureRecognizer
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNManualRecognizer {
__weak RNGestureHandler *_gestureHandler;
BOOL _shouldSendBeginEvent;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_shouldSendBeginEvent = YES;
}
return self;
}
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
[super touchesBegan:touches withEvent:event];
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
if (_shouldSendBeginEvent) {
[_gestureHandler handleGesture:self];
_shouldSendBeginEvent = NO;
}
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
if ([self shouldFail]) {
self.state = (self.state == UIGestureRecognizerStatePossible) ? UIGestureRecognizerStateFailed
: UIGestureRecognizerStateCancelled;
[self reset];
}
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
- (void)reset
{
[_gestureHandler.pointerTracker reset];
[super reset];
[_gestureHandler reset];
_shouldSendBeginEvent = YES;
}
- (BOOL)shouldFail
{
if (_gestureHandler.shouldCancelWhenOutside && ![_gestureHandler containsPointInView]) {
return YES;
} else {
return NO;
}
}
@end
@implementation RNManualGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNManualRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
@end
#else
@implementation RNManualGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
RCTLogWarn(@"ManualGestureHandler is not supported on macOS");
if ((self = [super initWithTag:tag])) {
_recognizer = [NSGestureRecognizer alloc];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,15 @@
//
// RNNativeViewHandler.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNDummyGestureRecognizer : UIGestureRecognizer
@end
@interface RNNativeViewGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,231 @@
//
// RNNativeViewHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNNativeViewHandler.h"
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#endif
#import <React/RCTConvert.h>
#import <React/UIView+React.h>
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTScrollViewComponentView.h>
#else
#import <React/RCTScrollView.h>
#endif // RCT_NEW_ARCH_ENABLED
#if !TARGET_OS_OSX
#pragma mark RNDummyGestureRecognizer
@implementation RNDummyGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
}
return self;
}
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
self.state = UIGestureRecognizerStateFailed;
[self reset];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
self.state = UIGestureRecognizerStateCancelled;
[self reset];
}
- (void)reset
{
[_gestureHandler.pointerTracker reset];
[super reset];
[_gestureHandler reset];
}
@end
#pragma mark RNNativeViewGestureHandler
@implementation RNNativeViewGestureHandler {
BOOL _shouldActivateOnStart;
BOOL _disallowInterruption;
}
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNDummyGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (void)configure:(NSDictionary *)config
{
[super configure:config];
_shouldActivateOnStart = [RCTConvert BOOL:config[@"shouldActivateOnStart"]];
_disallowInterruption = [RCTConvert BOOL:config[@"disallowInterruption"]];
}
- (void)bindToView:(UIView *)view
{
// For UIControl based views (UIButton, UISwitch) we provide special handling that would allow
// for properties like `disallowInterruption` to work.
if ([view isKindOfClass:[UIControl class]]) {
UIControl *control = (UIControl *)view;
[control addTarget:self action:@selector(handleTouchDown:forEvent:) forControlEvents:UIControlEventTouchDown];
[control addTarget:self
action:@selector(handleTouchUpOutside:forEvent:)
forControlEvents:UIControlEventTouchUpOutside];
[control addTarget:self
action:@selector(handleTouchUpInside:forEvent:)
forControlEvents:UIControlEventTouchUpInside];
[control addTarget:self action:@selector(handleDragExit:forEvent:) forControlEvents:UIControlEventTouchDragExit];
[control addTarget:self action:@selector(handleDragEnter:forEvent:) forControlEvents:UIControlEventTouchDragEnter];
[control addTarget:self action:@selector(handleTouchCancel:forEvent:) forControlEvents:UIControlEventTouchCancel];
} else {
[super bindToView:view];
}
// We can restore default scrollview behaviour to delay touches to scrollview's children
// because gesture handler system can handle cancellation of scroll recognizer when JS responder
// is set
#ifdef RCT_NEW_ARCH_ENABLED
if ([view isKindOfClass:[RCTScrollViewComponentView class]]) {
UIScrollView *scrollView = ((RCTScrollViewComponentView *)view).scrollView;
scrollView.delaysContentTouches = YES;
}
#else
if ([view isKindOfClass:[RCTScrollView class]]) {
// This part of the code is coupled with RN implementation of ScrollView native wrapper and
// we expect for RCTScrollView component to contain a subclass of UIScrollview as the only
// subview
UIScrollView *scrollView = [view.subviews objectAtIndex:0];
scrollView.delaysContentTouches = YES;
}
#endif // RCT_NEW_ARCH_ENABLED
}
- (void)handleTouchDown:(UIView *)sender forEvent:(UIEvent *)event
{
[self setCurrentPointerType:event];
[self reset];
if (_disallowInterruption) {
// When `disallowInterruption` is set we cancel all gesture handlers when this UIControl
// gets DOWN event
for (RNGHUITouch *touch in [event allTouches]) {
for (UIGestureRecognizer *recogn in [touch gestureRecognizers]) {
recogn.enabled = NO;
recogn.enabled = YES;
}
}
}
[self sendEventsInState:RNGestureHandlerStateActive
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
}
- (void)handleTouchUpOutside:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateEnd
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO withPointerType:_pointerType]];
}
- (void)handleTouchUpInside:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateEnd
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
}
- (void)handleDragExit:(UIView *)sender forEvent:(UIEvent *)event
{
// Pointer is moved outside of the view bounds, we cancel button when `shouldCancelWhenOutside` is set
if (self.shouldCancelWhenOutside) {
UIControl *control = (UIControl *)sender;
[control cancelTrackingWithEvent:event];
[self sendEventsInState:RNGestureHandlerStateEnd
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO withPointerType:_pointerType]];
} else {
[self sendEventsInState:RNGestureHandlerStateActive
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO withPointerType:_pointerType]];
}
}
- (void)handleDragEnter:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateActive
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withPointerType:_pointerType]];
}
- (void)handleTouchCancel:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateCancelled
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO withPointerType:_pointerType]];
}
@end
#else
#pragma mark RNDummyGestureRecognizer
@implementation RNDummyGestureRecognizer
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)];
return self;
}
@end
#pragma mark RNNativeViewGestureHandler
@implementation RNNativeViewGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
RCTLogWarn(@"NativeViewGestureHandler is not supported on macOS");
if ((self = [super initWithTag:tag])) {
_recognizer = [NSGestureRecognizer alloc];
}
return self;
}
@end
#endif

View File

@@ -0,0 +1,12 @@
//
// RNPanHandler.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNPanGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,421 @@
//
// RNPanHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNPanHandler.h"
#if TARGET_OS_OSX
@interface RNBetterPanGestureRecognizer : NSPanGestureRecognizer
#else
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface RNBetterPanGestureRecognizer : UIPanGestureRecognizer
#endif
@property (nonatomic) CGFloat minDistSq;
@property (nonatomic) CGFloat minVelocityX;
@property (nonatomic) CGFloat minVelocityY;
@property (nonatomic) CGFloat minVelocitySq;
@property (nonatomic) CGFloat activeOffsetXStart;
@property (nonatomic) CGFloat activeOffsetXEnd;
@property (nonatomic) CGFloat failOffsetXStart;
@property (nonatomic) CGFloat failOffsetXEnd;
@property (nonatomic) CGFloat activeOffsetYStart;
@property (nonatomic) CGFloat activeOffsetYEnd;
@property (nonatomic) CGFloat failOffsetYStart;
@property (nonatomic) CGFloat failOffsetYEnd;
@property (nonatomic) CGFloat activateAfterLongPress;
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNBetterPanGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
#if !TARGET_OS_OSX
NSUInteger _realMinimumNumberOfTouches;
#endif
BOOL _hasCustomActivationCriteria;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_minDistSq = NAN;
_minVelocityX = NAN;
_minVelocityY = NAN;
_minVelocitySq = NAN;
_activeOffsetXStart = NAN;
_activeOffsetXEnd = NAN;
_failOffsetXStart = NAN;
_failOffsetXEnd = NAN;
_activeOffsetYStart = NAN;
_activeOffsetYEnd = NAN;
_failOffsetYStart = NAN;
_failOffsetYEnd = NAN;
_activateAfterLongPress = NAN;
_hasCustomActivationCriteria = NO;
#if !TARGET_OS_TV && !TARGET_OS_OSX
_realMinimumNumberOfTouches = self.minimumNumberOfTouches;
#endif
}
return self;
}
- (void)triggerAction
{
[_gestureHandler handleGesture:self];
}
#if !TARGET_OS_OSX
- (void)setMinimumNumberOfTouches:(NSUInteger)minimumNumberOfTouches
{
_realMinimumNumberOfTouches = minimumNumberOfTouches;
}
#endif
- (void)activateAfterLongPress
{
self.state = UIGestureRecognizerStateBegan;
// Send event in ACTIVE state because UIGestureRecognizerStateBegan is mapped to RNGestureHandlerStateBegan
[_gestureHandler handleGesture:self inState:RNGestureHandlerStateActive];
}
- (void)interactionsBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (touches.count == 0) {
[_gestureHandler reset];
}
#if !TARGET_OS_TV && !TARGET_OS_OSX
if (_hasCustomActivationCriteria) {
// We use "minimumNumberOfTouches" property to prevent pan handler from recognizing
// the gesture too early before we are sure that all criteria (e.g. minimum distance
// etc. are met)
super.minimumNumberOfTouches = 20;
} else {
super.minimumNumberOfTouches = _realMinimumNumberOfTouches;
}
#endif
#if TARGET_OS_OSX
[super mouseDown:event];
#else
[super touchesBegan:touches withEvent:event];
#endif
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
[self triggerAction];
if (!isnan(_activateAfterLongPress)) {
[self performSelector:@selector(activateAfterLongPress) withObject:nil afterDelay:_activateAfterLongPress];
}
}
- (void)interactionsMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStatePossible && [self shouldFailUnderCustomCriteria]) {
self.state = UIGestureRecognizerStateFailed;
[self triggerAction];
return;
}
if ((self.state == UIGestureRecognizerStatePossible || self.state == UIGestureRecognizerStateChanged)) {
if (_gestureHandler.shouldCancelWhenOutside && ![_gestureHandler containsPointInView]) {
// If the previous recognizer state is UIGestureRecognizerStateChanged
// then UIGestureRecognizer's sate machine will only transition to
// UIGestureRecognizerStateCancelled even if you set the state to
// UIGestureRecognizerStateFailed here. Making the behavior explicit.
self.state = (self.state == UIGestureRecognizerStatePossible) ? UIGestureRecognizerStateFailed
: UIGestureRecognizerStateCancelled;
[self reset];
return;
}
}
#if !TARGET_OS_TV && !TARGET_OS_OSX
if (_hasCustomActivationCriteria && self.state == UIGestureRecognizerStatePossible &&
[self shouldActivateUnderCustomCriteria]) {
super.minimumNumberOfTouches = _realMinimumNumberOfTouches;
if ([self numberOfTouches] >= _realMinimumNumberOfTouches) {
self.state = UIGestureRecognizerStateBegan;
[self setTranslation:CGPointMake(0, 0) inView:self.view];
}
}
#endif
}
- (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
}
- (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
#if TARGET_OS_OSX
- (void)mouseDown:(NSEvent *)event
{
[_gestureHandler setCurrentPointerTypeToMouse];
// super call was moved to interactionsBegan method to keep the
// original order of calls
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
}
- (void)mouseDragged:(NSEvent *)event
{
[super mouseDragged:event];
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
}
- (void)mouseUp:(NSEvent *)event
{
[super mouseUp:event];
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
}
#else
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
// super call was moved to interactionsBegan method to keep the
// original order of calls
[self interactionsBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self interactionsMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self interactionsEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self interactionsCancelled:touches withEvent:event];
}
#endif
- (void)reset
{
[self triggerAction];
[_gestureHandler.pointerTracker reset];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(activateAfterLongPress) object:nil];
self.enabled = YES;
[super reset];
[_gestureHandler reset];
}
- (void)updateHasCustomActivationCriteria
{
_hasCustomActivationCriteria = !isnan(_minDistSq) || !isnan(_minVelocityX) || !isnan(_minVelocityY) ||
!isnan(_minVelocitySq) || !isnan(_activeOffsetXStart) || !isnan(_activeOffsetXEnd) ||
!isnan(_activeOffsetYStart) || !isnan(_activeOffsetYEnd);
}
- (BOOL)shouldFailUnderCustomCriteria
{
#if TARGET_OS_OSX
CGPoint trans = [self translationInView:self.view.window.contentView];
#else
CGPoint trans = [self translationInView:self.view.window];
#endif
// Apple docs say that 10 units is the default allowable movement for UILongPressGestureRecognizer
if (!isnan(_activateAfterLongPress) && trans.x * trans.x + trans.y * trans.y > 100) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(activateAfterLongPress) object:nil];
return YES;
}
if (!isnan(_failOffsetXStart) && trans.x < _failOffsetXStart) {
return YES;
}
if (!isnan(_failOffsetXEnd) && trans.x > _failOffsetXEnd) {
return YES;
}
if (!isnan(_failOffsetYStart) && trans.y < _failOffsetYStart) {
return YES;
}
if (!isnan(_failOffsetYEnd) && trans.y > _failOffsetYEnd) {
return YES;
}
return NO;
}
- (BOOL)shouldActivateUnderCustomCriteria
{
#if TARGET_OS_OSX
CGPoint trans = [self translationInView:self.view.window.contentView];
#else
CGPoint trans = [self translationInView:self.view.window];
#endif
if (!isnan(_activeOffsetXStart) && trans.x < _activeOffsetXStart) {
return YES;
}
if (!isnan(_activeOffsetXEnd) && trans.x > _activeOffsetXEnd) {
return YES;
}
if (!isnan(_activeOffsetYStart) && trans.y < _activeOffsetYStart) {
return YES;
}
if (!isnan(_activeOffsetYEnd) && trans.y > _activeOffsetYEnd) {
return YES;
}
if (TEST_MIN_IF_NOT_NAN(VEC_LEN_SQ(trans), _minDistSq)) {
return YES;
}
CGPoint velocity = [self velocityInView:self.view];
if (TEST_MIN_IF_NOT_NAN(velocity.x, _minVelocityX)) {
return YES;
}
if (TEST_MIN_IF_NOT_NAN(velocity.y, _minVelocityY)) {
return YES;
}
if (TEST_MIN_IF_NOT_NAN(VEC_LEN_SQ(velocity), _minVelocitySq)) {
return YES;
}
return NO;
}
@end
@implementation RNPanGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNBetterPanGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (void)resetConfig
{
[super resetConfig];
RNBetterPanGestureRecognizer *recognizer = (RNBetterPanGestureRecognizer *)_recognizer;
recognizer.minVelocityX = NAN;
recognizer.minVelocityY = NAN;
recognizer.activeOffsetXStart = NAN;
recognizer.activeOffsetXEnd = NAN;
recognizer.failOffsetXStart = NAN;
recognizer.failOffsetXEnd = NAN;
recognizer.activeOffsetYStart = NAN;
recognizer.activeOffsetYEnd = NAN;
recognizer.failOffsetYStart = NAN;
recognizer.failOffsetYStart = NAN;
recognizer.failOffsetYEnd = NAN;
#if !TARGET_OS_OSX && !TARGET_OS_TV && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400
if (@available(iOS 13.4, *)) {
recognizer.allowedScrollTypesMask = 0;
}
#endif
#if !TARGET_OS_OSX && !TARGET_OS_TV
recognizer.minimumNumberOfTouches = 1;
recognizer.maximumNumberOfTouches = NSUIntegerMax;
#endif
recognizer.minDistSq = NAN;
recognizer.minVelocitySq = NAN;
recognizer.activateAfterLongPress = NAN;
}
- (void)configure:(NSDictionary *)config
{
[super configure:config];
RNBetterPanGestureRecognizer *recognizer = (RNBetterPanGestureRecognizer *)_recognizer;
APPLY_FLOAT_PROP(minVelocityX);
APPLY_FLOAT_PROP(minVelocityY);
APPLY_FLOAT_PROP(activeOffsetXStart);
APPLY_FLOAT_PROP(activeOffsetXEnd);
APPLY_FLOAT_PROP(failOffsetXStart);
APPLY_FLOAT_PROP(failOffsetXEnd);
APPLY_FLOAT_PROP(activeOffsetYStart);
APPLY_FLOAT_PROP(activeOffsetYEnd);
APPLY_FLOAT_PROP(failOffsetYStart);
APPLY_FLOAT_PROP(failOffsetYEnd);
#if !TARGET_OS_OSX && !TARGET_OS_TV && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130400
if (@available(iOS 13.4, *)) {
bool enableTrackpadTwoFingerGesture = [RCTConvert BOOL:config[@"enableTrackpadTwoFingerGesture"]];
if (enableTrackpadTwoFingerGesture) {
recognizer.allowedScrollTypesMask = UIScrollTypeMaskAll;
}
}
APPLY_NAMED_INT_PROP(minimumNumberOfTouches, @"minPointers");
APPLY_NAMED_INT_PROP(maximumNumberOfTouches, @"maxPointers");
#endif
id prop = config[@"minDist"];
if (prop != nil) {
CGFloat dist = [RCTConvert CGFloat:prop];
recognizer.minDistSq = dist * dist;
}
prop = config[@"minVelocity"];
if (prop != nil) {
CGFloat velocity = [RCTConvert CGFloat:prop];
recognizer.minVelocitySq = velocity * velocity;
}
prop = config[@"activateAfterLongPress"];
if (prop != nil) {
recognizer.activateAfterLongPress = [RCTConvert CGFloat:prop] / 1000.0;
recognizer.minDistSq = MAX(100, recognizer.minDistSq);
}
[recognizer updateHasCustomActivationCriteria];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
RNGestureHandlerState savedState = _lastState;
BOOL shouldBegin = [super gestureRecognizerShouldBegin:gestureRecognizer];
_lastState = savedState;
return shouldBegin;
}
#if TARGET_OS_OSX
- (RNGestureHandlerEventExtraData *)eventExtraData:(NSPanGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forPan:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window.contentView]
withTranslation:[recognizer translationInView:recognizer.view.window.contentView]
withVelocity:[recognizer velocityInView:recognizer.view.window.contentView]
withNumberOfTouches:1
withPointerType:RNGestureHandlerMouse];
}
#else
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIPanGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forPan:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withTranslation:[recognizer translationInView:recognizer.view.window]
withVelocity:[recognizer velocityInView:recognizer.view.window]
withNumberOfTouches:recognizer.numberOfTouches
withPointerType:_pointerType];
}
#endif
@end

View File

@@ -0,0 +1,12 @@
//
// RNPinchHandler.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNPinchGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,186 @@
//
// RNPinchHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNPinchHandler.h"
#if !TARGET_OS_TV
#if TARGET_OS_OSX
@interface RNBetterPinchRecognizer : NSMagnificationGestureRecognizer {
CGFloat prevMagnification;
NSTimeInterval prevTime;
}
@property (nonatomic, readonly) CGFloat velocity;
#else
@interface RNBetterPinchRecognizer : UIPinchGestureRecognizer
#endif
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNBetterPinchRecognizer {
__weak RNGestureHandler *_gestureHandler;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:self action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
}
#if TARGET_OS_OSX
prevMagnification = 0;
prevTime = 0;
#endif
return self;
}
- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
if (self.state == UIGestureRecognizerStateBegan) {
#if TARGET_OS_OSX
self.magnification = 1;
#else
self.scale = 1;
#endif
}
[_gestureHandler handleGesture:recognizer];
}
- (void)interactionsBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
}
- (void)interactionsMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
}
- (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
}
- (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
#if TARGET_OS_OSX
- (void)magnifyWithEvent:(NSEvent *)event
{
[super magnifyWithEvent:event];
switch (self.state) {
case NSGestureRecognizerStateBegan:
[_gestureHandler setCurrentPointerTypeToMouse];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
break;
case NSGestureRecognizerStateChanged:
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
break;
case NSGestureRecognizerStateEnded:
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
break;
case NSGestureRecognizerStateCancelled:
[self interactionsCancelled:[NSSet setWithObject:event] withEvent:event];
break;
}
_velocity = (self.magnification - prevMagnification) / ((event.timestamp - prevTime) * 1000);
prevMagnification = self.magnification;
prevTime = event.timestamp;
}
#else
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
[super touchesBegan:touches withEvent:event];
[self interactionsBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self interactionsMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self interactionsEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self interactionsCancelled:touches withEvent:event];
}
#endif
- (void)reset
{
[_gestureHandler.pointerTracker reset];
[super reset];
[_gestureHandler reset];
}
@end
#endif
@implementation RNPinchGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
#if !TARGET_OS_TV
_recognizer = [[RNBetterPinchRecognizer alloc] initWithGestureHandler:self];
#endif
}
return self;
}
#if !TARGET_OS_TV
#if TARGET_OS_OSX
- (RNGestureHandlerEventExtraData *)eventExtraData:(NSMagnificationGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forPinch:recognizer.magnification
withFocalPoint:[recognizer locationInView:recognizer.view]
withVelocity:((RNBetterPinchRecognizer *)recognizer).velocity
withNumberOfTouches:2
withPointerType:RNGestureHandlerMouse];
}
#else
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIPinchGestureRecognizer *)recognizer
{
CGPoint accumulatedPoint = CGPointZero;
for (int i = 0; i < recognizer.numberOfTouches; i++) {
CGPoint location = [recognizer locationOfTouch:i inView:recognizer.view];
accumulatedPoint.x += location.x;
accumulatedPoint.y += location.y;
}
CGPoint focalPoint =
CGPointMake(accumulatedPoint.x / recognizer.numberOfTouches, accumulatedPoint.y / recognizer.numberOfTouches);
return [RNGestureHandlerEventExtraData forPinch:recognizer.scale
withFocalPoint:focalPoint
withVelocity:recognizer.velocity
withNumberOfTouches:recognizer.numberOfTouches
withPointerType:_pointerType];
}
#endif
#endif // !TARGET_OS_TV
@end

View File

@@ -0,0 +1,12 @@
//
// RNRotationHandler.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNRotationGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,169 @@
//
// RNRotationHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNRotationHandler.h"
#if !TARGET_OS_TV
#if TARGET_OS_OSX
@interface RNBetterRotationRecognizer : NSRotationGestureRecognizer {
CGFloat prevRotation;
NSTimeInterval prevTime;
}
@property (nonatomic, readonly) CGFloat velocity;
#else
@interface RNBetterRotationRecognizer : UIRotationGestureRecognizer
#endif
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNBetterRotationRecognizer {
__weak RNGestureHandler *_gestureHandler;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:self action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
}
#if TARGET_OS_OSX
prevRotation = 0;
prevTime = 0;
#endif
return self;
}
- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
if (self.state == UIGestureRecognizerStateBegan) {
self.rotation = 0;
}
[_gestureHandler handleGesture:recognizer];
}
- (void)interactionsBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
}
- (void)interactionsMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
}
- (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
}
- (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
}
#if TARGET_OS_OSX
- (void)rotateWithEvent:(NSEvent *)event
{
[super rotateWithEvent:event];
switch (self.state) {
case NSGestureRecognizerStateBegan:
[_gestureHandler setCurrentPointerTypeToMouse];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
break;
case NSGestureRecognizerStateChanged:
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
break;
case NSGestureRecognizerStateEnded:
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
break;
case NSGestureRecognizerStateCancelled:
[self interactionsCancelled:[NSSet setWithObject:event] withEvent:event];
break;
}
_velocity = (self.rotation - prevRotation) / ((event.timestamp - prevTime) * 1000);
prevRotation = self.rotation;
prevTime = event.timestamp;
}
#else
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
[super touchesBegan:touches withEvent:event];
[self interactionsBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self interactionsMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self interactionsEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self interactionsCancelled:touches withEvent:event];
}
#endif
- (void)reset
{
[_gestureHandler.pointerTracker reset];
[super reset];
[_gestureHandler reset];
}
@end
#endif
@implementation RNRotationGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
#if !TARGET_OS_TV
_recognizer = [[RNBetterRotationRecognizer alloc] initWithGestureHandler:self];
#endif
}
return self;
}
#if !TARGET_OS_TV
#if TARGET_OS_OSX
- (RNGestureHandlerEventExtraData *)eventExtraData:(NSRotationGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forRotation:recognizer.rotation
withAnchorPoint:[recognizer locationInView:recognizer.view]
withVelocity:((RNBetterRotationRecognizer *)recognizer).velocity
withNumberOfTouches:2
withPointerType:RNGestureHandlerMouse];
}
#else
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIRotationGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData forRotation:recognizer.rotation
withAnchorPoint:[recognizer locationInView:recognizer.view]
withVelocity:recognizer.velocity
withNumberOfTouches:recognizer.numberOfTouches
withPointerType:_pointerType];
}
#endif
#endif // !TARGET_OS_TV
@end

View File

@@ -0,0 +1,12 @@
//
// RNTapHandler.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNTapGestureHandler : RNGestureHandler
@end

View File

@@ -0,0 +1,337 @@
//
// RNTapHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNTapHandler.h"
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#endif
#import <React/RCTConvert.h>
// 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.
@interface RNBetterTapGestureRecognizer : UIGestureRecognizer
@property (nonatomic) NSUInteger numberOfTaps;
@property (nonatomic) NSTimeInterval maxDelay;
@property (nonatomic) NSTimeInterval maxDuration;
@property (nonatomic) CGFloat maxDistSq;
@property (nonatomic) CGFloat maxDeltaX;
@property (nonatomic) CGFloat maxDeltaY;
@property (nonatomic) NSInteger minPointers;
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
@end
@implementation RNBetterTapGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
NSUInteger _tapsSoFar;
CGPoint _initPosition;
NSInteger _maxNumberOfTouches;
}
static const NSUInteger defaultNumberOfTaps = 1;
static const NSInteger defaultMinPointers = 1;
static const CGFloat defaultMaxDelay = 0.2;
static const NSTimeInterval defaultMaxDuration = 0.5;
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_tapsSoFar = 0;
_numberOfTaps = defaultNumberOfTaps;
_minPointers = defaultMinPointers;
_maxDelay = defaultMaxDelay;
_maxDuration = defaultMaxDuration;
_maxDeltaX = NAN;
_maxDeltaY = NAN;
_maxDistSq = NAN;
}
return self;
}
- (void)triggerAction
{
[_gestureHandler handleGesture:self];
}
- (void)cancel
{
self.enabled = NO;
}
- (void)interactionsBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesBegan:touches withEvent:event];
if (_tapsSoFar == 0) {
// this recognizer sends UNDETERMINED -> BEGAN state change event before gestureRecognizerShouldBegin
// is called (it resets the gesture handler), making it send whatever the last known state as oldState
// in the event. If we reset it here it correctly sends UNDETERMINED as oldState.
[_gestureHandler reset];
#if TARGET_OS_OSX
_initPosition = [self locationInView:self.view.window.contentView];
#else
_initPosition = [self locationInView:self.view.window];
#endif
}
_tapsSoFar++;
if (_tapsSoFar) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
}
NSInteger numberOfTouches = [touches count];
if (numberOfTouches > _maxNumberOfTouches) {
_maxNumberOfTouches = numberOfTouches;
}
if (!isnan(_maxDuration)) {
[self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDuration];
}
self.state = UIGestureRecognizerStatePossible;
[self triggerAction];
}
- (void)interactionsMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesMoved:touches withEvent:event];
NSInteger numberOfTouches = [touches count];
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];
}
- (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesEnded:touches withEvent:event];
if (_numberOfTaps == _tapsSoFar && _maxNumberOfTouches >= _minPointers) {
self.state = UIGestureRecognizerStateEnded;
[self reset];
} else {
[self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDelay];
}
}
- (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[_gestureHandler.pointerTracker touchesCancelled:touches withEvent:event];
self.state = UIGestureRecognizerStateCancelled;
[self reset];
}
#if TARGET_OS_OSX
- (void)mouseDown:(NSEvent *)event
{
[_gestureHandler setCurrentPointerTypeToMouse];
[super mouseDown:event];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
}
- (void)rightMouseDown:(NSEvent *)event
{
[super rightMouseDown:event];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
}
- (void)mouseDragged:(NSEvent *)event
{
[super mouseDragged:event];
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
}
- (void)rightMouseDragged:(NSEvent *)event
{
[super rightMouseDragged:event];
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
}
- (void)mouseUp:(NSEvent *)event
{
[super mouseUp:event];
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
}
- (void)rightMouseUp:(NSEvent *)event
{
[super rightMouseUp:event];
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
}
#else
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[_gestureHandler setCurrentPointerType:event];
[super touchesBegan:touches withEvent:event];
[self interactionsBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self interactionsMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self interactionsEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self interactionsCancelled:touches withEvent:event];
}
#endif
- (CGPoint)translationInView
{
#if TARGET_OS_OSX
CGPoint currentPosition = [self locationInView:self.view.window.contentView];
#else
CGPoint currentPosition = [self locationInView:self.view.window];
#endif
return CGPointMake(currentPosition.x - _initPosition.x, currentPosition.y - _initPosition.y);
}
- (BOOL)shouldFailUnderCustomCriteria
{
if (_gestureHandler.shouldCancelWhenOutside) {
if (![_gestureHandler containsPointInView]) {
return YES;
}
}
CGPoint trans = [self translationInView];
if (TEST_MAX_IF_NOT_NAN(fabs(trans.x), _maxDeltaX)) {
return YES;
}
if (TEST_MAX_IF_NOT_NAN(fabs(trans.y), _maxDeltaY)) {
return YES;
}
if (TEST_MAX_IF_NOT_NAN(fabs(trans.y * trans.y + trans.x * trans.x), _maxDistSq)) {
return YES;
}
return NO;
}
- (void)reset
{
if (self.state == UIGestureRecognizerStateFailed) {
[self triggerAction];
}
[_gestureHandler.pointerTracker reset];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
_tapsSoFar = 0;
_maxNumberOfTouches = 0;
self.enabled = YES;
[super reset];
[_gestureHandler reset];
}
@end
@implementation RNTapGestureHandler {
RNGestureHandlerEventExtraData *_lastData;
}
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNBetterTapGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (void)resetConfig
{
[super resetConfig];
RNBetterTapGestureRecognizer *recognizer = (RNBetterTapGestureRecognizer *)_recognizer;
recognizer.numberOfTaps = defaultNumberOfTaps;
recognizer.minPointers = defaultMinPointers;
recognizer.maxDeltaX = NAN;
recognizer.maxDeltaY = NAN;
recognizer.maxDelay = defaultMaxDelay;
recognizer.maxDuration = defaultMaxDuration;
recognizer.maxDistSq = NAN;
}
- (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];
recognizer.maxDistSq = dist * dist;
}
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateEnded) {
return _lastData;
}
_lastData = [super eventExtraData:recognizer];
return _lastData;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// UNDETERMINED -> BEGAN state change event is sent before this method is called,
// in RNGestureHandler it resets _lastSatate variable, making is seem like handler
// went from UNDETERMINED to BEGAN and then from UNDETERMINED to ACTIVE.
// This way we preserve _lastState between events and keep correct state flow.
RNGestureHandlerState savedState = _lastState;
BOOL shouldBegin = [super gestureRecognizerShouldBegin:gestureRecognizer];
_lastState = savedState;
return shouldBegin;
}
@end

View File

@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, RNGHTouchEventType) {
RNGHTouchEventTypeUndetermined = 0,
RNGHTouchEventTypePointerDown,
RNGHTouchEventTypePointerMove,
RNGHTouchEventTypePointerUp,
RNGHTouchEventTypeCancelled,
};

View File

@@ -0,0 +1,27 @@
#if !TARGET_OS_OSX
#import <UIKit/UIKit.h>
typedef UIView RNGHUIView;
typedef UITouch RNGHUITouch;
#define RNGHGestureRecognizerStateFailed UIGestureRecognizerStateFailed;
#define RNGHGestureRecognizerStatePossible UIGestureRecognizerStatePossible;
#define RNGHGestureRecognizerStateCancelled UIGestureRecognizerStateCancelled;
#define RNGHGestureRecognizerStateBegan UIGestureRecognizerStateBegan;
#define RNGHGestureRecognizerStateEnded UIGestureRecognizerStateEnded;
#else // TARGET_OS_OSX [
#import <React/RCTUIKit.h>
typedef RCTUIView RNGHUIView;
typedef RCTUITouch RNGHUITouch;
#define RNGHGestureRecognizerStateFailed NSGestureRecognizerStateFailed;
#define RNGHGestureRecognizerStatePossible NSGestureRecognizerStatePossible;
#define RNGHGestureRecognizerStateCancelled NSGestureRecognizerStateCancelled;
#define RNGHGestureRecognizerStateBegan NSGestureRecognizerStateBegan;
#define RNGHGestureRecognizerStateEnded NSGestureRecognizerStateEnded;
#endif // ] TARGET_OS_OSX

View File

@@ -0,0 +1,99 @@
#import "RNGHUIKit.h"
#import "RNGestureHandlerActionType.h"
#import "RNGestureHandlerDirection.h"
#import "RNGestureHandlerEvents.h"
#import "RNGestureHandlerPointerTracker.h"
#import "RNGestureHandlerPointerType.h"
#import "RNGestureHandlerState.h"
#import <Foundation/Foundation.h>
#import <React/RCTConvert.h>
#define VEC_LEN_SQ(pt) (pt.x * pt.x + pt.y * pt.y)
#define TEST_MIN_IF_NOT_NAN(value, limit) \
(!isnan(limit) && ((limit < 0 && value <= limit) || (limit >= 0 && value >= limit)))
#define TEST_MAX_IF_NOT_NAN(value, max) (!isnan(max) && ((max < 0 && value < max) || (max >= 0 && value > max)))
#define APPLY_PROP(recognizer, config, type, prop, propName) \
do { \
id value = config[propName]; \
if (value != nil) { \
recognizer.prop = [RCTConvert type:value]; \
} \
} while (0)
#define APPLY_FLOAT_PROP(prop) \
do { \
APPLY_PROP(recognizer, config, CGFloat, prop, @ #prop); \
} while (0)
#define APPLY_INT_PROP(prop) \
do { \
APPLY_PROP(recognizer, config, NSInteger, prop, @ #prop); \
} while (0)
#define APPLY_NAMED_INT_PROP(prop, propName) \
do { \
APPLY_PROP(recognizer, config, NSInteger, prop, propName); \
} while (0)
@protocol RNGestureHandlerEventEmitter
- (void)sendEvent:(nonnull RNGestureHandlerStateChange *)event withActionType:(RNGestureHandlerActionType)actionType;
@end
@protocol RNRootViewGestureRecognizerDelegate <UIGestureRecognizerDelegate>
- (void)gestureRecognizer:(nullable UIGestureRecognizer *)gestureRecognizer
didActivateInViewWithTouchHandler:(nullable RNGHUIView *)viewWithTouchHandler;
@end
@interface RNGestureHandler : NSObject <UIGestureRecognizerDelegate> {
@protected
UIGestureRecognizer *_recognizer;
@protected
RNGestureHandlerState _lastState;
@protected
NSInteger _pointerType;
}
+ (nullable RNGestureHandler *)findGestureHandlerByRecognizer:(nonnull UIGestureRecognizer *)recognizer;
- (nonnull instancetype)initWithTag:(nonnull NSNumber *)tag;
@property (nonatomic, readonly, nonnull) NSNumber *tag;
@property (nonatomic, weak, nullable) id<RNGestureHandlerEventEmitter> emitter;
@property (nonatomic, readonly, nullable) UIGestureRecognizer *recognizer;
@property (nonatomic, readonly, nullable) RNGestureHandlerPointerTracker *pointerTracker;
@property (nonatomic) BOOL enabled;
@property (nonatomic) RNGestureHandlerActionType actionType;
@property (nonatomic) BOOL shouldCancelWhenOutside;
@property (nonatomic) BOOL needsPointerData;
@property (nonatomic) BOOL manualActivation;
- (void)bindToView:(nonnull RNGHUIView *)view;
- (void)unbindFromView;
- (void)resetConfig NS_REQUIRES_SUPER;
- (void)configure:(nullable NSDictionary *)config NS_REQUIRES_SUPER;
- (void)handleGesture:(nonnull id)recognizer;
- (void)handleGesture:(nonnull id)recognizer inState:(RNGestureHandlerState)state;
- (BOOL)containsPointInView;
- (RNGestureHandlerState)state;
- (nullable RNGestureHandlerEventExtraData *)eventExtraData:(nonnull id)recognizer;
- (void)stopActivationBlocker;
- (void)reset;
- (void)sendEventsInState:(RNGestureHandlerState)state
forViewWithTag:(nonnull NSNumber *)reactTag
withExtraData:(nonnull RNGestureHandlerEventExtraData *)extraData;
- (void)sendEvent:(nonnull RNGestureHandlerStateChange *)event;
- (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(nonnull NSNumber *)reactTag;
#if !TARGET_OS_OSX
- (void)setCurrentPointerType:(nonnull UIEvent *)event;
#else
- (void)setCurrentPointerTypeToMouse;
#endif
@end

View File

@@ -0,0 +1,556 @@
#import "RNGestureHandler.h"
#import "RNManualActivationRecognizer.h"
#import "Handlers/RNNativeViewHandler.h"
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#endif
#import <React/UIView+React.h>
@interface UIGestureRecognizer (GestureHandler)
@property (nonatomic, readonly) RNGestureHandler *gestureHandler;
@end
@implementation UIGestureRecognizer (GestureHandler)
- (RNGestureHandler *)gestureHandler
{
id delegate = self.delegate;
if ([delegate isKindOfClass:[RNGestureHandler class]]) {
return (RNGestureHandler *)delegate;
}
return nil;
}
@end
typedef struct RNGHHitSlop {
CGFloat top, left, bottom, right, width, height;
} RNGHHitSlop;
static RNGHHitSlop RNGHHitSlopEmpty = {NAN, NAN, NAN, NAN, NAN, NAN};
#define RNGH_HIT_SLOP_GET(key) (prop[key] == nil ? NAN : [prop[key] doubleValue])
#define RNGH_HIT_SLOP_IS_SET(hitSlop) \
(!isnan(hitSlop.left) || !isnan(hitSlop.right) || !isnan(hitSlop.top) || !isnan(hitSlop.bottom))
#define RNGH_HIT_SLOP_INSET(key) (isnan(hitSlop.key) ? 0. : hitSlop.key)
CGRect RNGHHitSlopInsetRect(CGRect rect, RNGHHitSlop hitSlop)
{
rect.origin.x -= RNGH_HIT_SLOP_INSET(left);
rect.origin.y -= RNGH_HIT_SLOP_INSET(top);
if (!isnan(hitSlop.width)) {
if (!isnan(hitSlop.right)) {
rect.origin.x = rect.size.width - hitSlop.width + RNGH_HIT_SLOP_INSET(right);
}
rect.size.width = hitSlop.width;
} else {
rect.size.width += (RNGH_HIT_SLOP_INSET(left) + RNGH_HIT_SLOP_INSET(right));
}
if (!isnan(hitSlop.height)) {
if (!isnan(hitSlop.bottom)) {
rect.origin.y = rect.size.height - hitSlop.height + RNGH_HIT_SLOP_INSET(bottom);
}
rect.size.height = hitSlop.height;
} else {
rect.size.height += (RNGH_HIT_SLOP_INSET(top) + RNGH_HIT_SLOP_INSET(bottom));
}
return rect;
}
static NSHashTable<RNGestureHandler *> *allGestureHandlers;
@implementation RNGestureHandler {
RNGestureHandlerPointerTracker *_pointerTracker;
RNGestureHandlerState _state;
RNManualActivationRecognizer *_manualActivationRecognizer;
NSArray<NSNumber *> *_handlersToWaitFor;
NSArray<NSNumber *> *_handlersThatShouldWait;
NSArray<NSNumber *> *_simultaneousHandlers;
RNGHHitSlop _hitSlop;
uint16_t _eventCoalescingKey;
}
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super init])) {
_pointerTracker = [[RNGestureHandlerPointerTracker alloc] initWithGestureHandler:self];
_tag = tag;
_lastState = RNGestureHandlerStateUndetermined;
_hitSlop = RNGHHitSlopEmpty;
_state = RNGestureHandlerStateBegan;
_manualActivationRecognizer = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
allGestureHandlers = [NSHashTable weakObjectsHashTable];
});
[allGestureHandlers addObject:self];
}
return self;
}
- (void)resetConfig
{
self.enabled = YES;
self.manualActivation = NO;
_shouldCancelWhenOutside = NO;
_handlersToWaitFor = nil;
_simultaneousHandlers = nil;
_handlersThatShouldWait = nil;
_hitSlop = RNGHHitSlopEmpty;
_needsPointerData = NO;
#if !TARGET_OS_OSX
_recognizer.cancelsTouchesInView = YES;
#endif
}
- (void)configure:(NSDictionary *)config
{
[self resetConfig];
_handlersToWaitFor = [RCTConvert NSNumberArray:config[@"waitFor"]];
_simultaneousHandlers = [RCTConvert NSNumberArray:config[@"simultaneousHandlers"]];
_handlersThatShouldWait = [RCTConvert NSNumberArray:config[@"blocksHandlers"]];
id prop = config[@"enabled"];
if (prop != nil) {
self.enabled = [RCTConvert BOOL:prop];
}
prop = config[@"shouldCancelWhenOutside"];
if (prop != nil) {
_shouldCancelWhenOutside = [RCTConvert BOOL:prop];
}
#if !TARGET_OS_OSX
prop = config[@"cancelsTouchesInView"];
if (prop != nil) {
_recognizer.cancelsTouchesInView = [RCTConvert BOOL:prop];
}
#endif
prop = config[@"needsPointerData"];
if (prop != nil) {
_needsPointerData = [RCTConvert BOOL:prop];
}
prop = config[@"manualActivation"];
if (prop != nil) {
self.manualActivation = [RCTConvert BOOL:prop];
}
prop = config[@"hitSlop"];
if ([prop isKindOfClass:[NSNumber class]]) {
_hitSlop.left = _hitSlop.right = _hitSlop.top = _hitSlop.bottom = [prop doubleValue];
} else if (prop != nil) {
_hitSlop.left = _hitSlop.right = RNGH_HIT_SLOP_GET(@"horizontal");
_hitSlop.top = _hitSlop.bottom = RNGH_HIT_SLOP_GET(@"vertical");
_hitSlop.left = RNGH_HIT_SLOP_GET(@"left");
_hitSlop.right = RNGH_HIT_SLOP_GET(@"right");
_hitSlop.top = RNGH_HIT_SLOP_GET(@"top");
_hitSlop.bottom = RNGH_HIT_SLOP_GET(@"bottom");
_hitSlop.width = RNGH_HIT_SLOP_GET(@"width");
_hitSlop.height = RNGH_HIT_SLOP_GET(@"height");
if (isnan(_hitSlop.left) && isnan(_hitSlop.right) && !isnan(_hitSlop.width)) {
RCTLogError(@"When width is set one of left or right pads need to be defined");
}
if (!isnan(_hitSlop.width) && !isnan(_hitSlop.left) && !isnan(_hitSlop.right)) {
RCTLogError(@"Cannot have all of left, right and width defined");
}
if (isnan(_hitSlop.top) && isnan(_hitSlop.bottom) && !isnan(_hitSlop.height)) {
RCTLogError(@"When height is set one of top or bottom pads need to be defined");
}
if (!isnan(_hitSlop.height) && !isnan(_hitSlop.top) && !isnan(_hitSlop.bottom)) {
RCTLogError(@"Cannot have all of top, bottom and height defined");
}
}
}
- (void)setEnabled:(BOOL)enabled
{
_enabled = enabled;
self.recognizer.enabled = enabled;
}
#if !TARGET_OS_OSX
- (void)setCurrentPointerType:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
switch (touch.type) {
case UITouchTypeDirect:
_pointerType = RNGestureHandlerTouch;
break;
case UITouchTypePencil:
_pointerType = RNGestureHandlerStylus;
break;
case UITouchTypeIndirectPointer:
_pointerType = RNGestureHandlerMouse;
break;
default:
_pointerType = RNGestureHandlerOtherPointer;
break;
}
}
#else
- (void)setCurrentPointerTypeToMouse
{
_pointerType = RNGestureHandlerMouse;
}
#endif
- (UITouchType)getPointerType
{
return _pointerType;
}
- (void)bindToView:(RNGHUIView *)view
{
#if !TARGET_OS_OSX
view.userInteractionEnabled = YES;
#endif
self.recognizer.delegate = self;
[view addGestureRecognizer:self.recognizer];
[self bindManualActivationToView:view];
}
- (void)unbindFromView
{
[self.recognizer.view removeGestureRecognizer:self.recognizer];
self.recognizer.delegate = nil;
[self unbindManualActivation];
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIGestureRecognizer *)recognizer
{
#if TARGET_OS_OSX
return [RNGestureHandlerEventExtraData forPosition:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window.contentView]
withNumberOfTouches:1
withPointerType:RNGestureHandlerMouse];
#else
return [RNGestureHandlerEventExtraData forPosition:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withNumberOfTouches:recognizer.numberOfTouches
withPointerType:_pointerType];
#endif
}
- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
// it may happen that the gesture recognizer is reset after it's been unbound from the view,
// it that recognizer tried to send event, the app would crash because the target of the event
// would be nil.
if (recognizer.view.reactTag == nil) {
return;
}
_state = [self recognizerState];
[self handleGesture:recognizer inState:_state];
}
- (void)handleGesture:(UIGestureRecognizer *)recognizer inState:(RNGestureHandlerState)state
{
_state = state;
RNGestureHandlerEventExtraData *eventData = [self eventExtraData:recognizer];
[self sendEventsInState:self.state forViewWithTag:recognizer.view.reactTag withExtraData:eventData];
}
- (void)sendEventsInState:(RNGestureHandlerState)state
forViewWithTag:(nonnull NSNumber *)reactTag
withExtraData:(RNGestureHandlerEventExtraData *)extraData
{
if (state != _lastState) {
// don't send change events from END to FAILED or CANCELLED, this may happen when gesture is ended in `onTouchesUp`
// callback
if (_lastState == RNGestureHandlerStateEnd &&
(state == RNGestureHandlerStateFailed || state == RNGestureHandlerStateCancelled)) {
return;
}
// Recognizers don't respect manually changing their state (that happens when we are activating handler
// under custom conditions). If we send a custom event in state ACTIVE and the recognizer will later update its
// state, we will end up sending ACTIVE->BEGAN and BEGAN->ACTIVE chain. To prevent this, we simply detect the first
// weird state change and stop it (then we don't update _lastState), so the second call ends up without state change
// and is fine.
if (state == RNGestureHandlerStateBegan && _lastState == RNGestureHandlerStateActive) {
return;
}
if (state == RNGestureHandlerStateActive) {
// Generate a unique coalescing-key each time the gesture-handler becomes active. All events will have
// the same coalescing-key allowing RCTEventDispatcher to coalesce RNGestureHandlerEvents when events are
// generated faster than they can be treated by JS thread
static uint16_t nextEventCoalescingKey = 0;
self->_eventCoalescingKey = nextEventCoalescingKey++;
} else if (state == RNGestureHandlerStateEnd && _lastState != RNGestureHandlerStateActive && !_manualActivation) {
id event = [[RNGestureHandlerStateChange alloc] initWithReactTag:reactTag
handlerTag:_tag
state:RNGestureHandlerStateActive
prevState:_lastState
extraData:extraData];
[self sendEvent:event];
_lastState = RNGestureHandlerStateActive;
}
id stateEvent = [[RNGestureHandlerStateChange alloc] initWithReactTag:reactTag
handlerTag:_tag
state:state
prevState:_lastState
extraData:extraData];
[self sendEvent:stateEvent];
_lastState = state;
}
if (state == RNGestureHandlerStateActive) {
id touchEvent = [[RNGestureHandlerEvent alloc] initWithReactTag:reactTag
handlerTag:_tag
state:state
extraData:extraData
coalescingKey:self->_eventCoalescingKey];
[self sendEvent:touchEvent];
}
}
- (void)sendEvent:(RNGestureHandlerStateChange *)event
{
[self.emitter sendEvent:event withActionType:self.actionType];
}
- (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(NSNumber *)reactTag
{
id extraData = [RNGestureHandlerEventExtraData forEventType:_pointerTracker.eventType
withChangedPointers:_pointerTracker.changedPointersData
withAllPointers:_pointerTracker.allPointersData
withNumberOfTouches:_pointerTracker.trackedPointersCount
withPointerType:_pointerType];
id event = [[RNGestureHandlerEvent alloc] initWithReactTag:reactTag
handlerTag:_tag
state:state
extraData:extraData
coalescingKey:[_tag intValue]];
[self.emitter sendEvent:event withActionType:self.actionType];
}
- (RNGestureHandlerState)recognizerState
{
switch (_recognizer.state) {
case UIGestureRecognizerStateBegan:
case UIGestureRecognizerStatePossible:
return RNGestureHandlerStateBegan;
case UIGestureRecognizerStateEnded:
return RNGestureHandlerStateEnd;
case UIGestureRecognizerStateFailed:
return RNGestureHandlerStateFailed;
case UIGestureRecognizerStateCancelled:
return RNGestureHandlerStateCancelled;
case UIGestureRecognizerStateChanged:
return RNGestureHandlerStateActive;
}
return RNGestureHandlerStateUndetermined;
}
- (RNGestureHandlerState)state
{
// instead of mapping state of the recognizer directly, use value mapped when handleGesture was
// called, making it correct while awaiting for another handler failure
return _state;
}
#pragma mark Manual activation
- (void)stopActivationBlocker
{
if (_manualActivationRecognizer != nil) {
[_manualActivationRecognizer fail];
}
}
- (void)setManualActivation:(BOOL)manualActivation
{
_manualActivation = manualActivation;
if (manualActivation) {
_manualActivationRecognizer = [[RNManualActivationRecognizer alloc] initWithGestureHandler:self];
if (_recognizer.view != nil) {
[_recognizer.view addGestureRecognizer:_manualActivationRecognizer];
}
} else if (_manualActivationRecognizer != nil) {
[_manualActivationRecognizer.view removeGestureRecognizer:_manualActivationRecognizer];
_manualActivationRecognizer = nil;
}
}
- (void)bindManualActivationToView:(RNGHUIView *)view
{
if (_manualActivationRecognizer != nil) {
[view addGestureRecognizer:_manualActivationRecognizer];
}
}
- (void)unbindManualActivation
{
if (_manualActivationRecognizer != nil) {
[_manualActivationRecognizer.view removeGestureRecognizer:_manualActivationRecognizer];
}
}
#pragma mark UIGestureRecognizerDelegate
+ (RNGestureHandler *)findGestureHandlerByRecognizer:(UIGestureRecognizer *)recognizer
{
RNGestureHandler *handler = recognizer.gestureHandler;
if (handler != nil) {
return handler;
}
// We may try to extract "DummyGestureHandler" in case when "otherGestureRecognizer" belongs to
// a native view being wrapped with "NativeViewGestureHandler"
RNGHUIView *reactView = recognizer.view;
while (reactView != nil && reactView.reactTag == nil) {
reactView = reactView.superview;
}
for (UIGestureRecognizer *recognizer in reactView.gestureRecognizers) {
if ([recognizer isKindOfClass:[RNDummyGestureRecognizer class]]) {
return recognizer.gestureHandler;
}
}
return nil;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
if ([handler isKindOfClass:[RNNativeViewGestureHandler class]]) {
for (NSNumber *handlerTag in handler->_handlersToWaitFor) {
if ([_tag isEqual:handlerTag]) {
return YES;
}
}
}
if (handler != nil) {
for (NSNumber *handlerTag in _handlersThatShouldWait) {
if ([handler.tag isEqual:handlerTag]) {
return YES;
}
}
}
return NO;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([_handlersToWaitFor count]) {
RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
if (handler != nil) {
for (NSNumber *handlerTag in _handlersToWaitFor) {
if ([handler.tag isEqual:handlerTag]) {
return YES;
}
}
}
}
return NO;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if (_recognizer.state == UIGestureRecognizerStateBegan && _recognizer.state == UIGestureRecognizerStatePossible) {
return YES;
}
RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
if (handler != nil) {
if ([_simultaneousHandlers count]) {
for (NSNumber *handlerTag in _simultaneousHandlers) {
if ([handler.tag isEqual:handlerTag]) {
return YES;
}
}
}
if (handler->_simultaneousHandlers) {
for (NSNumber *handlerTag in handler->_simultaneousHandlers) {
if ([self.tag isEqual:handlerTag]) {
return YES;
}
}
}
}
return NO;
}
- (void)reset
{
// do not reset states while gesture is tracking pointers, as gestureRecognizerShouldBegin
// might be called after some pointers are down, and after state manipulation by the user.
// Pointer tracker calls this method when it resets, and in that case it no longer tracks
// any pointers, thus entering this if
if (!_needsPointerData || _pointerTracker.trackedPointersCount == 0) {
_lastState = RNGestureHandlerStateUndetermined;
_state = RNGestureHandlerStateBegan;
}
}
- (BOOL)containsPointInView
{
CGPoint pt = [_recognizer locationInView:_recognizer.view];
CGRect hitFrame = RNGHHitSlopInsetRect(_recognizer.view.bounds, _hitSlop);
return CGRectContainsPoint(hitFrame, pt);
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([_handlersToWaitFor count]) {
for (RNGestureHandler *handler in [allGestureHandlers allObjects]) {
if (handler != nil &&
(handler.state == RNGestureHandlerStateActive ||
handler->_recognizer.state == UIGestureRecognizerStateBegan)) {
for (NSNumber *handlerTag in _handlersToWaitFor) {
if ([handler.tag isEqual:handlerTag]) {
return NO;
}
}
}
}
}
[self reset];
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(RNGHUITouch *)touch
{
// If hitSlop is set we use it to determine if a given gesture recognizer should start processing
// touch stream. This only works for negative values of hitSlop as this method won't be triggered
// unless touch startes in the bounds of the attached view. To acheve similar effect with positive
// values of hitSlop one should set hitSlop for the underlying view. This limitation is due to the
// fact that hitTest method is only available at the level of UIView
if (RNGH_HIT_SLOP_IS_SET(_hitSlop)) {
#if TARGET_OS_OSX
CGPoint location = [gestureRecognizer.view convertPoint:touch.locationInWindow fromView:nil];
#else
CGPoint location = [touch locationInView:gestureRecognizer.view];
#endif
CGRect hitFrame = RNGHHitSlopInsetRect(gestureRecognizer.view.bounds, _hitSlop);
return CGRectContainsPoint(hitFrame, location);
}
return YES;
}
@end

View File

@@ -0,0 +1,690 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
446E7FDE1ED57CA6009282E7 /* RNGestureHandlerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 446E7FDD1ED57CA6009282E7 /* RNGestureHandlerModule.m */; };
446E7FE61ED6E177009282E7 /* RNGestureHandlerModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 446E7FDC1ED57CA6009282E7 /* RNGestureHandlerModule.h */; };
446E7FE71ED6E177009282E7 /* RNGestureHandlerState.h in Headers */ = {isa = PBXBuildFile; fileRef = 44384A781ECDE0DB006BAD02 /* RNGestureHandlerState.h */; };
446E7FF61ED89A31009282E7 /* RNGestureHandlerEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = 446E7FF51ED89A31009282E7 /* RNGestureHandlerEvents.h */; };
446E7FF81ED89A4B009282E7 /* RNGestureHandlerEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 446E7FF71ED89A4B009282E7 /* RNGestureHandlerEvents.m */; };
448802DD1F6803DF00018214 /* RNGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 448802DB1F6803DF00018214 /* RNGestureHandler.h */; };
448802DE1F6803DF00018214 /* RNGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 448802DC1F6803DF00018214 /* RNGestureHandler.m */; };
44AEC7111F8F9B6C0086889F /* RNRootViewGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7101F8F9B6C0086889F /* RNRootViewGestureRecognizer.h */; };
44AEC7131F8F9B770086889F /* RNRootViewGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7121F8F9B770086889F /* RNRootViewGestureRecognizer.m */; };
44AEC7151F8F9BEF0086889F /* RNGestureHandlerRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7141F8F9BEF0086889F /* RNGestureHandlerRegistry.h */; };
44AEC7171F8F9C090086889F /* RNGestureHandlerRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7161F8F9C090086889F /* RNGestureHandlerRegistry.m */; };
44AEC71F1F8FA0700086889F /* RNGestureHandlerButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC71D1F8FA0700086889F /* RNGestureHandlerButton.h */; };
44AEC7201F8FA0700086889F /* RNGestureHandlerButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC71E1F8FA0700086889F /* RNGestureHandlerButton.m */; };
44AEC72F1F8FA1270086889F /* RNLongPressHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7231F8FA1270086889F /* RNLongPressHandler.h */; };
44AEC7301F8FA1270086889F /* RNLongPressHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7241F8FA1270086889F /* RNLongPressHandler.m */; };
44AEC7311F8FA1270086889F /* RNNativeViewHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7251F8FA1270086889F /* RNNativeViewHandler.h */; };
44AEC7321F8FA1270086889F /* RNNativeViewHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7261F8FA1270086889F /* RNNativeViewHandler.m */; };
44AEC7331F8FA1270086889F /* RNPanHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7271F8FA1270086889F /* RNPanHandler.h */; };
44AEC7341F8FA1270086889F /* RNPanHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7281F8FA1270086889F /* RNPanHandler.m */; };
44AEC7351F8FA1270086889F /* RNPinchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7291F8FA1270086889F /* RNPinchHandler.h */; };
44AEC7361F8FA1270086889F /* RNPinchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC72A1F8FA1270086889F /* RNPinchHandler.m */; };
44AEC7371F8FA1270086889F /* RNRotationHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC72B1F8FA1270086889F /* RNRotationHandler.h */; };
44AEC7381F8FA1270086889F /* RNRotationHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC72C1F8FA1270086889F /* RNRotationHandler.m */; };
44AEC7391F8FA1270086889F /* RNTapHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC72D1F8FA1270086889F /* RNTapHandler.h */; };
44AEC73A1F8FA1270086889F /* RNTapHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC72E1F8FA1270086889F /* RNTapHandler.m */; };
44BE34481F1E1AAA008679D1 /* RNGestureHandlerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 44BE34471F1E1AAA008679D1 /* RNGestureHandlerManager.m */; };
44BE344A1F1E1ABA008679D1 /* RNGestureHandlerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 44BE34491F1E1ABA008679D1 /* RNGestureHandlerManager.h */; };
660F46742080D8F700B7B50D /* RNGestureHandlerDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 660F46732080D8F600B7B50D /* RNGestureHandlerDirection.h */; };
668E083C21BDD70900EDDF40 /* RNForceTouchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 668E083A21BDD70900EDDF40 /* RNForceTouchHandler.h */; };
668E083D21BDD70900EDDF40 /* RNForceTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E083B21BDD70900EDDF40 /* RNForceTouchHandler.m */; };
66A291D5207D032400809C27 /* RNFlingHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A291D3207D032400809C27 /* RNFlingHandler.m */; };
66A291D6207D032400809C27 /* RNFlingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 66A291D4207D032400809C27 /* RNFlingHandler.h */; };
B5C32A0E220C603B000FFB8D /* RNGestureHandlerButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC71E1F8FA0700086889F /* RNGestureHandlerButton.m */; };
B5C32A0F220C603B000FFB8D /* RNForceTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E083B21BDD70900EDDF40 /* RNForceTouchHandler.m */; };
B5C32A10220C603B000FFB8D /* RNGestureHandlerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 44BE34471F1E1AAA008679D1 /* RNGestureHandlerManager.m */; };
B5C32A11220C603B000FFB8D /* RNLongPressHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7241F8FA1270086889F /* RNLongPressHandler.m */; };
B5C32A12220C603B000FFB8D /* RNGestureHandlerEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 446E7FF71ED89A4B009282E7 /* RNGestureHandlerEvents.m */; };
B5C32A13220C603B000FFB8D /* RNPanHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7281F8FA1270086889F /* RNPanHandler.m */; };
B5C32A14220C603B000FFB8D /* RNGestureHandlerRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7161F8F9C090086889F /* RNGestureHandlerRegistry.m */; };
B5C32A15220C603B000FFB8D /* RNGestureHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 448802DC1F6803DF00018214 /* RNGestureHandler.m */; };
B5C32A16220C603B000FFB8D /* RNTapHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC72E1F8FA1270086889F /* RNTapHandler.m */; };
B5C32A17220C603B000FFB8D /* RNGestureHandlerModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 446E7FDD1ED57CA6009282E7 /* RNGestureHandlerModule.m */; };
B5C32A18220C603B000FFB8D /* RNRotationHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC72C1F8FA1270086889F /* RNRotationHandler.m */; };
B5C32A19220C603B000FFB8D /* RNNativeViewHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7261F8FA1270086889F /* RNNativeViewHandler.m */; };
B5C32A1A220C603B000FFB8D /* RNRootViewGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC7121F8F9B770086889F /* RNRootViewGestureRecognizer.m */; };
B5C32A1B220C603B000FFB8D /* RNFlingHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 66A291D3207D032400809C27 /* RNFlingHandler.m */; };
B5C32A1C220C603B000FFB8D /* RNPinchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 44AEC72A1F8FA1270086889F /* RNPinchHandler.m */; };
B5C32A1E220C603B000FFB8D /* RNForceTouchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 668E083A21BDD70900EDDF40 /* RNForceTouchHandler.h */; };
B5C32A1F220C603B000FFB8D /* RNFlingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 66A291D4207D032400809C27 /* RNFlingHandler.h */; };
B5C32A20220C603B000FFB8D /* RNLongPressHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7231F8FA1270086889F /* RNLongPressHandler.h */; };
B5C32A21220C603B000FFB8D /* RNGestureHandlerModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 446E7FDC1ED57CA6009282E7 /* RNGestureHandlerModule.h */; };
B5C32A22220C603B000FFB8D /* RNPinchHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7291F8FA1270086889F /* RNPinchHandler.h */; };
B5C32A23220C603B000FFB8D /* RNGestureHandlerRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7141F8F9BEF0086889F /* RNGestureHandlerRegistry.h */; };
B5C32A24220C603B000FFB8D /* RNGestureHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 448802DB1F6803DF00018214 /* RNGestureHandler.h */; };
B5C32A25220C603B000FFB8D /* RNRotationHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC72B1F8FA1270086889F /* RNRotationHandler.h */; };
B5C32A26220C603B000FFB8D /* RNPanHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7271F8FA1270086889F /* RNPanHandler.h */; };
B5C32A27220C603B000FFB8D /* RNGestureHandlerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 44BE34491F1E1ABA008679D1 /* RNGestureHandlerManager.h */; };
B5C32A28220C603B000FFB8D /* RNGestureHandlerEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = 446E7FF51ED89A31009282E7 /* RNGestureHandlerEvents.h */; };
B5C32A29220C603B000FFB8D /* RNTapHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC72D1F8FA1270086889F /* RNTapHandler.h */; };
B5C32A2A220C603B000FFB8D /* RNRootViewGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7101F8F9B6C0086889F /* RNRootViewGestureRecognizer.h */; };
B5C32A2B220C603B000FFB8D /* RNNativeViewHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC7251F8FA1270086889F /* RNNativeViewHandler.h */; };
B5C32A2C220C603B000FFB8D /* RNGestureHandlerState.h in Headers */ = {isa = PBXBuildFile; fileRef = 44384A781ECDE0DB006BAD02 /* RNGestureHandlerState.h */; };
B5C32A2D220C603B000FFB8D /* RNGestureHandlerButton.h in Headers */ = {isa = PBXBuildFile; fileRef = 44AEC71D1F8FA0700086889F /* RNGestureHandlerButton.h */; };
B5C32A2E220C603B000FFB8D /* RNGestureHandlerDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 660F46732080D8F600B7B50D /* RNGestureHandlerDirection.h */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B5C32A30220C603B000FFB8D /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNGestureHandler.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNGestureHandler.a; sourceTree = BUILT_PRODUCTS_DIR; };
44384A781ECDE0DB006BAD02 /* RNGestureHandlerState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerState.h; sourceTree = "<group>"; };
446E7FDC1ED57CA6009282E7 /* RNGestureHandlerModule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerModule.h; sourceTree = "<group>"; };
446E7FDD1ED57CA6009282E7 /* RNGestureHandlerModule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNGestureHandlerModule.m; sourceTree = "<group>"; };
446E7FF51ED89A31009282E7 /* RNGestureHandlerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerEvents.h; sourceTree = "<group>"; };
446E7FF71ED89A4B009282E7 /* RNGestureHandlerEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNGestureHandlerEvents.m; sourceTree = "<group>"; };
448802DB1F6803DF00018214 /* RNGestureHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandler.h; sourceTree = "<group>"; };
448802DC1F6803DF00018214 /* RNGestureHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNGestureHandler.m; sourceTree = "<group>"; };
44AEC7101F8F9B6C0086889F /* RNRootViewGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNRootViewGestureRecognizer.h; sourceTree = "<group>"; };
44AEC7121F8F9B770086889F /* RNRootViewGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNRootViewGestureRecognizer.m; sourceTree = "<group>"; };
44AEC7141F8F9BEF0086889F /* RNGestureHandlerRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerRegistry.h; sourceTree = "<group>"; };
44AEC7161F8F9C090086889F /* RNGestureHandlerRegistry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNGestureHandlerRegistry.m; sourceTree = "<group>"; };
44AEC71D1F8FA0700086889F /* RNGestureHandlerButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerButton.h; sourceTree = "<group>"; };
44AEC71E1F8FA0700086889F /* RNGestureHandlerButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNGestureHandlerButton.m; sourceTree = "<group>"; };
44AEC7231F8FA1270086889F /* RNLongPressHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNLongPressHandler.h; path = Handlers/RNLongPressHandler.h; sourceTree = "<group>"; };
44AEC7241F8FA1270086889F /* RNLongPressHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNLongPressHandler.m; path = Handlers/RNLongPressHandler.m; sourceTree = "<group>"; };
44AEC7251F8FA1270086889F /* RNNativeViewHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNNativeViewHandler.h; path = Handlers/RNNativeViewHandler.h; sourceTree = "<group>"; };
44AEC7261F8FA1270086889F /* RNNativeViewHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNNativeViewHandler.m; path = Handlers/RNNativeViewHandler.m; sourceTree = "<group>"; };
44AEC7271F8FA1270086889F /* RNPanHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPanHandler.h; path = Handlers/RNPanHandler.h; sourceTree = "<group>"; };
44AEC7281F8FA1270086889F /* RNPanHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPanHandler.m; path = Handlers/RNPanHandler.m; sourceTree = "<group>"; };
44AEC7291F8FA1270086889F /* RNPinchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNPinchHandler.h; path = Handlers/RNPinchHandler.h; sourceTree = "<group>"; };
44AEC72A1F8FA1270086889F /* RNPinchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNPinchHandler.m; path = Handlers/RNPinchHandler.m; sourceTree = "<group>"; };
44AEC72B1F8FA1270086889F /* RNRotationHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNRotationHandler.h; path = Handlers/RNRotationHandler.h; sourceTree = "<group>"; };
44AEC72C1F8FA1270086889F /* RNRotationHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNRotationHandler.m; path = Handlers/RNRotationHandler.m; sourceTree = "<group>"; };
44AEC72D1F8FA1270086889F /* RNTapHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNTapHandler.h; path = Handlers/RNTapHandler.h; sourceTree = "<group>"; };
44AEC72E1F8FA1270086889F /* RNTapHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNTapHandler.m; path = Handlers/RNTapHandler.m; sourceTree = "<group>"; };
44BE34471F1E1AAA008679D1 /* RNGestureHandlerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNGestureHandlerManager.m; sourceTree = "<group>"; };
44BE34491F1E1ABA008679D1 /* RNGestureHandlerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerManager.h; sourceTree = "<group>"; };
660F46732080D8F600B7B50D /* RNGestureHandlerDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNGestureHandlerDirection.h; sourceTree = "<group>"; };
668E083A21BDD70900EDDF40 /* RNForceTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNForceTouchHandler.h; path = Handlers/RNForceTouchHandler.h; sourceTree = "<group>"; };
668E083B21BDD70900EDDF40 /* RNForceTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNForceTouchHandler.m; path = Handlers/RNForceTouchHandler.m; sourceTree = "<group>"; };
66A291D3207D032400809C27 /* RNFlingHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RNFlingHandler.m; path = Handlers/RNFlingHandler.m; sourceTree = "<group>"; };
66A291D4207D032400809C27 /* RNFlingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RNFlingHandler.h; path = Handlers/RNFlingHandler.h; sourceTree = "<group>"; };
B5C32A36220C603B000FFB8D /* libRNGestureHandler-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRNGestureHandler-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B5C32A2F220C603B000FFB8D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRNGestureHandler.a */,
);
name = Products;
sourceTree = "<group>";
};
44AEC7221F8FA1150086889F /* Handlers */ = {
isa = PBXGroup;
children = (
668E083A21BDD70900EDDF40 /* RNForceTouchHandler.h */,
668E083B21BDD70900EDDF40 /* RNForceTouchHandler.m */,
66A291D4207D032400809C27 /* RNFlingHandler.h */,
66A291D3207D032400809C27 /* RNFlingHandler.m */,
44AEC7231F8FA1270086889F /* RNLongPressHandler.h */,
44AEC7241F8FA1270086889F /* RNLongPressHandler.m */,
44AEC7251F8FA1270086889F /* RNNativeViewHandler.h */,
44AEC7261F8FA1270086889F /* RNNativeViewHandler.m */,
44AEC7271F8FA1270086889F /* RNPanHandler.h */,
44AEC7281F8FA1270086889F /* RNPanHandler.m */,
44AEC7291F8FA1270086889F /* RNPinchHandler.h */,
44AEC72A1F8FA1270086889F /* RNPinchHandler.m */,
44AEC72B1F8FA1270086889F /* RNRotationHandler.h */,
44AEC72C1F8FA1270086889F /* RNRotationHandler.m */,
44AEC72D1F8FA1270086889F /* RNTapHandler.h */,
44AEC72E1F8FA1270086889F /* RNTapHandler.m */,
);
name = Handlers;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
660F46732080D8F600B7B50D /* RNGestureHandlerDirection.h */,
44AEC7221F8FA1150086889F /* Handlers */,
44AEC7161F8F9C090086889F /* RNGestureHandlerRegistry.m */,
44AEC7141F8F9BEF0086889F /* RNGestureHandlerRegistry.h */,
44AEC7121F8F9B770086889F /* RNRootViewGestureRecognizer.m */,
44AEC7101F8F9B6C0086889F /* RNRootViewGestureRecognizer.h */,
448802DB1F6803DF00018214 /* RNGestureHandler.h */,
448802DC1F6803DF00018214 /* RNGestureHandler.m */,
44BE34491F1E1ABA008679D1 /* RNGestureHandlerManager.h */,
44BE34471F1E1AAA008679D1 /* RNGestureHandlerManager.m */,
446E7FF71ED89A4B009282E7 /* RNGestureHandlerEvents.m */,
446E7FF51ED89A31009282E7 /* RNGestureHandlerEvents.h */,
446E7FDC1ED57CA6009282E7 /* RNGestureHandlerModule.h */,
446E7FDD1ED57CA6009282E7 /* RNGestureHandlerModule.m */,
44384A781ECDE0DB006BAD02 /* RNGestureHandlerState.h */,
44AEC71D1F8FA0700086889F /* RNGestureHandlerButton.h */,
44AEC71E1F8FA0700086889F /* RNGestureHandlerButton.m */,
134814211AA4EA7D00B7C361 /* Products */,
B5C32A36220C603B000FFB8D /* libRNGestureHandler-tvOS.a */,
);
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
446E7FE51ED6DBD8009282E7 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
668E083C21BDD70900EDDF40 /* RNForceTouchHandler.h in Headers */,
66A291D6207D032400809C27 /* RNFlingHandler.h in Headers */,
44AEC72F1F8FA1270086889F /* RNLongPressHandler.h in Headers */,
446E7FE61ED6E177009282E7 /* RNGestureHandlerModule.h in Headers */,
44AEC7351F8FA1270086889F /* RNPinchHandler.h in Headers */,
44AEC7151F8F9BEF0086889F /* RNGestureHandlerRegistry.h in Headers */,
448802DD1F6803DF00018214 /* RNGestureHandler.h in Headers */,
44AEC7371F8FA1270086889F /* RNRotationHandler.h in Headers */,
44AEC7331F8FA1270086889F /* RNPanHandler.h in Headers */,
44BE344A1F1E1ABA008679D1 /* RNGestureHandlerManager.h in Headers */,
446E7FF61ED89A31009282E7 /* RNGestureHandlerEvents.h in Headers */,
44AEC7391F8FA1270086889F /* RNTapHandler.h in Headers */,
44AEC7111F8F9B6C0086889F /* RNRootViewGestureRecognizer.h in Headers */,
44AEC7311F8FA1270086889F /* RNNativeViewHandler.h in Headers */,
446E7FE71ED6E177009282E7 /* RNGestureHandlerState.h in Headers */,
44AEC71F1F8FA0700086889F /* RNGestureHandlerButton.h in Headers */,
660F46742080D8F700B7B50D /* RNGestureHandlerDirection.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B5C32A1D220C603B000FFB8D /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
B5C32A1E220C603B000FFB8D /* RNForceTouchHandler.h in Headers */,
B5C32A1F220C603B000FFB8D /* RNFlingHandler.h in Headers */,
B5C32A20220C603B000FFB8D /* RNLongPressHandler.h in Headers */,
B5C32A21220C603B000FFB8D /* RNGestureHandlerModule.h in Headers */,
B5C32A22220C603B000FFB8D /* RNPinchHandler.h in Headers */,
B5C32A23220C603B000FFB8D /* RNGestureHandlerRegistry.h in Headers */,
B5C32A24220C603B000FFB8D /* RNGestureHandler.h in Headers */,
B5C32A25220C603B000FFB8D /* RNRotationHandler.h in Headers */,
B5C32A26220C603B000FFB8D /* RNPanHandler.h in Headers */,
B5C32A27220C603B000FFB8D /* RNGestureHandlerManager.h in Headers */,
B5C32A28220C603B000FFB8D /* RNGestureHandlerEvents.h in Headers */,
B5C32A29220C603B000FFB8D /* RNTapHandler.h in Headers */,
B5C32A2A220C603B000FFB8D /* RNRootViewGestureRecognizer.h in Headers */,
B5C32A2B220C603B000FFB8D /* RNNativeViewHandler.h in Headers */,
B5C32A2C220C603B000FFB8D /* RNGestureHandlerState.h in Headers */,
B5C32A2D220C603B000FFB8D /* RNGestureHandlerButton.h in Headers */,
B5C32A2E220C603B000FFB8D /* RNGestureHandlerDirection.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RNGestureHandler */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNGestureHandler" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
446E7FE51ED6DBD8009282E7 /* Headers */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RNGestureHandler;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRNGestureHandler.a */;
productType = "com.apple.product-type.library.static";
};
B5C32A0C220C603B000FFB8D /* RNGestureHandler-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = B5C32A31220C603B000FFB8D /* Build configuration list for PBXNativeTarget "RNGestureHandler-tvOS" */;
buildPhases = (
B5C32A0D220C603B000FFB8D /* Sources */,
B5C32A1D220C603B000FFB8D /* Headers */,
B5C32A2F220C603B000FFB8D /* Frameworks */,
B5C32A30220C603B000FFB8D /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = "RNGestureHandler-tvOS";
productName = RCTDataManager;
productReference = B5C32A36220C603B000FFB8D /* libRNGestureHandler-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 610;
ORGANIZATIONNAME = "Software Mansion";
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNGestureHandler" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RNGestureHandler */,
B5C32A0C220C603B000FFB8D /* RNGestureHandler-tvOS */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
44AEC7201F8FA0700086889F /* RNGestureHandlerButton.m in Sources */,
668E083D21BDD70900EDDF40 /* RNForceTouchHandler.m in Sources */,
44BE34481F1E1AAA008679D1 /* RNGestureHandlerManager.m in Sources */,
44AEC7301F8FA1270086889F /* RNLongPressHandler.m in Sources */,
446E7FF81ED89A4B009282E7 /* RNGestureHandlerEvents.m in Sources */,
44AEC7341F8FA1270086889F /* RNPanHandler.m in Sources */,
44AEC7171F8F9C090086889F /* RNGestureHandlerRegistry.m in Sources */,
448802DE1F6803DF00018214 /* RNGestureHandler.m in Sources */,
44AEC73A1F8FA1270086889F /* RNTapHandler.m in Sources */,
446E7FDE1ED57CA6009282E7 /* RNGestureHandlerModule.m in Sources */,
44AEC7381F8FA1270086889F /* RNRotationHandler.m in Sources */,
44AEC7321F8FA1270086889F /* RNNativeViewHandler.m in Sources */,
44AEC7131F8F9B770086889F /* RNRootViewGestureRecognizer.m in Sources */,
66A291D5207D032400809C27 /* RNFlingHandler.m in Sources */,
44AEC7361F8FA1270086889F /* RNPinchHandler.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B5C32A0D220C603B000FFB8D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B5C32A0E220C603B000FFB8D /* RNGestureHandlerButton.m in Sources */,
B5C32A0F220C603B000FFB8D /* RNForceTouchHandler.m in Sources */,
B5C32A10220C603B000FFB8D /* RNGestureHandlerManager.m in Sources */,
B5C32A11220C603B000FFB8D /* RNLongPressHandler.m in Sources */,
B5C32A12220C603B000FFB8D /* RNGestureHandlerEvents.m in Sources */,
B5C32A13220C603B000FFB8D /* RNPanHandler.m in Sources */,
B5C32A14220C603B000FFB8D /* RNGestureHandlerRegistry.m in Sources */,
B5C32A15220C603B000FFB8D /* RNGestureHandler.m in Sources */,
B5C32A16220C603B000FFB8D /* RNTapHandler.m in Sources */,
B5C32A17220C603B000FFB8D /* RNGestureHandlerModule.m in Sources */,
B5C32A18220C603B000FFB8D /* RNRotationHandler.m in Sources */,
B5C32A19220C603B000FFB8D /* RNNativeViewHandler.m in Sources */,
B5C32A1A220C603B000FFB8D /* RNRootViewGestureRecognizer.m in Sources */,
B5C32A1B220C603B000FFB8D /* RNFlingHandler.m in Sources */,
B5C32A1C220C603B000FFB8D /* RNPinchHandler.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
3C75380331224952A9D19739 /* Testflight */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNGestureHandler;
SKIP_INSTALL = YES;
};
name = Testflight;
};
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNGestureHandler;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNGestureHandler;
SKIP_INSTALL = YES;
};
name = Release;
};
64C7ABFB934A41BFB09378ED /* Testflight */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Testflight;
};
6E1E231AAE4C4EB5B94B5418 /* Testflight */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Testflight;
};
8F4E4CFC1C3048678D76E403 /* Testflight */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNGestureHandler;
SKIP_INSTALL = YES;
};
name = Testflight;
};
B5C32A32220C603B000FFB8D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
};
name = Debug;
};
B5C32A33220C603B000FFB8D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
};
name = Release;
};
B5C32A34220C603B000FFB8D /* Testflight */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
};
name = Testflight;
};
B5C32A35220C603B000FFB8D /* Testflight */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNGestureHandler;
SKIP_INSTALL = YES;
};
name = Testflight;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNGestureHandler" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
6E1E231AAE4C4EB5B94B5418 /* Testflight */,
64C7ABFB934A41BFB09378ED /* Testflight */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNGestureHandler" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
3C75380331224952A9D19739 /* Testflight */,
8F4E4CFC1C3048678D76E403 /* Testflight */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B5C32A31220C603B000FFB8D /* Build configuration list for PBXNativeTarget "RNGestureHandler-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B5C32A32220C603B000FFB8D /* Debug */,
B5C32A33220C603B000FFB8D /* Release */,
B5C32A34220C603B000FFB8D /* Testflight */,
B5C32A35220C603B000FFB8D /* Testflight */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, RNGestureHandlerActionType) {
RNGestureHandlerActionTypeReanimatedWorklet = 1, // Reanimated worklet
RNGestureHandlerActionTypeNativeAnimatedEvent, // Animated.event with useNativeDriver: true
RNGestureHandlerActionTypeJSFunctionOldAPI, // JS function or Animated.event with useNativeDriver: false using old
// RNGH API
RNGestureHandlerActionTypeJSFunctionNewAPI, // JS function or Animated.event with useNativeDriver: false using new
// RNGH API
};

View File

@@ -0,0 +1,22 @@
//
// RNGestureHandlerButton.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
#if TARGET_OS_OSX
@interface RNGestureHandlerButton : NSControl
#else
@interface RNGestureHandlerButton : UIControl
#endif
/**
* Insets used when hit testing inside this view.
*/
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@property (nonatomic) BOOL userEnabled;
@end

View File

@@ -0,0 +1,85 @@
//
// RNGestureHandlerButton.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandlerButton.h"
#if !TARGET_OS_OSX
#import <UIKit/UIKit.h>
#endif
/**
* Gesture Handler Button components overrides standard mechanism used by RN
* to determine touch target, which normally would reurn the UIView that is placed
* as the deepest element in the view hierarchy.
* It's done this way as it allows for the actual target determination to run in JS
* where we can travers up the view ierarchy to find first element that want to became
* JS responder.
*
* Since we want to use native button (or actually a `UIControl`) we need to determine
* the target in native. This makes it impossible for JS responder based components to
* function as a subviews of the button component. Here we override `hitTest:withEvent:`
* method and we only determine the target to be either a subclass of `UIControl` or a
* view that has gesture recognizers registered.
*
* This "default" behaviour of target determinator should be sufficient in most of the
* cases as in fact it is not that common UI pattern to have many nested buttons (usually
* there are just two levels e.g. when you have clickable table cells with additional
* buttons). In cases when the default behaviour is insufficient it is recommended to use
* `TapGestureHandler` instead of a button which gives much better flexibility as far as
* controlling the touch flow.
*/
@implementation RNGestureHandlerButton
- (instancetype)init
{
self = [super init];
if (self) {
_hitTestEdgeInsets = UIEdgeInsetsZero;
_userEnabled = YES;
#if !TARGET_OS_TV && !TARGET_OS_OSX
[self setExclusiveTouch:YES];
#endif
}
return self;
}
- (BOOL)shouldHandleTouch:(RNGHUIView *)view
{
if ([view isKindOfClass:[RNGestureHandlerButton class]]) {
RNGestureHandlerButton *button = (RNGestureHandlerButton *)view;
return button.userEnabled;
}
#if !TARGET_OS_OSX
return [view isKindOfClass:[UIControl class]] || [view.gestureRecognizers count] > 0;
#else
return [view isKindOfClass:[NSControl class]] || [view.gestureRecognizers count] > 0;
#endif
}
#if !TARGET_OS_OSX
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
return [super pointInside:point withEvent:event];
}
CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
- (RNGHUIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
RNGHUIView *inner = [super hitTest:point withEvent:event];
while (inner && ![self shouldHandleTouch:inner]) {
inner = inner.superview;
}
return inner;
}
#endif
@end

View File

@@ -0,0 +1,17 @@
#ifdef RCT_NEW_ARCH_ENABLED
#import <UIKit/UIKit.h>
#import <React/RCTViewComponentView.h>
#import "RNGestureHandlerButton.h"
NS_ASSUME_NONNULL_BEGIN
@interface RNGestureHandlerButtonComponentView : RCTViewComponentView
@end
NS_ASSUME_NONNULL_END
#endif // RCT_NEW_ARCH_ENABLED

View File

@@ -0,0 +1,64 @@
#ifdef RCT_NEW_ARCH_ENABLED
#import "RNGestureHandlerButtonComponentView.h"
#import <React/RCTConversions.h>
#import <React/RCTFabricComponentsPlugins.h>
#import <react/renderer/components/rngesturehandler_codegen/ComponentDescriptors.h>
#import <react/renderer/components/rngesturehandler_codegen/EventEmitters.h>
#import <react/renderer/components/rngesturehandler_codegen/Props.h>
#import <react/renderer/components/rngesturehandler_codegen/RCTComponentViewHelpers.h>
#import "RNGestureHandlerButton.h"
using namespace facebook::react;
@interface RNGestureHandlerButtonComponentView () <RCTRNGestureHandlerButtonViewProtocol>
@end
@implementation RNGestureHandlerButtonComponentView {
RNGestureHandlerButton *_buttonView;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared<const RNGestureHandlerButtonProps>();
_props = defaultProps;
_buttonView = [[RNGestureHandlerButton alloc] initWithFrame:self.bounds];
self.contentView = _buttonView;
}
return self;
}
#pragma mark - RCTComponentViewProtocol
+ (ComponentDescriptorProvider)componentDescriptorProvider
{
return concreteComponentDescriptorProvider<RNGestureHandlerButtonComponentDescriptor>();
}
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
{
const auto &newProps = *std::static_pointer_cast<const RNGestureHandlerButtonProps>(props);
_buttonView.userEnabled = newProps.enabled;
#if !TARGET_OS_TV
_buttonView.exclusiveTouch = newProps.exclusive;
#endif
_buttonView.hitTestEdgeInsets = UIEdgeInsetsMake(
-newProps.hitSlop.top, -newProps.hitSlop.left, -newProps.hitSlop.bottom, -newProps.hitSlop.right);
[super updateProps:props oldProps:oldProps];
}
@end
Class<RCTComponentViewProtocol> RNGestureHandlerButtonCls(void)
{
return RNGestureHandlerButtonComponentView.class;
}
#endif // RCT_NEW_ARCH_ENABLED

View File

@@ -0,0 +1,5 @@
#import <React/RCTViewManager.h>
@interface RNGestureHandlerButtonManager : RCTViewManager
@end

View File

@@ -0,0 +1,36 @@
#import "RNGestureHandlerButtonManager.h"
#import "RNGestureHandlerButton.h"
@implementation RNGestureHandlerButtonManager
RCT_EXPORT_MODULE(RNGestureHandlerButton)
RCT_CUSTOM_VIEW_PROPERTY(enabled, BOOL, RNGestureHandlerButton)
{
view.userEnabled = json == nil ? YES : [RCTConvert BOOL:json];
}
#if !TARGET_OS_TV && !TARGET_OS_OSX
RCT_CUSTOM_VIEW_PROPERTY(exclusive, BOOL, RNGestureHandlerButton)
{
[view setExclusiveTouch:json == nil ? YES : [RCTConvert BOOL:json]];
}
#endif
RCT_CUSTOM_VIEW_PROPERTY(hitSlop, UIEdgeInsets, RNGestureHandlerButton)
{
if (json) {
UIEdgeInsets hitSlopInsets = [RCTConvert UIEdgeInsets:json];
view.hitTestEdgeInsets =
UIEdgeInsetsMake(-hitSlopInsets.top, -hitSlopInsets.left, -hitSlopInsets.bottom, -hitSlopInsets.right);
} else {
view.hitTestEdgeInsets = defaultView.hitTestEdgeInsets;
}
}
- (RNGHUIView *)view
{
return [RNGestureHandlerButton new];
}
@end

View File

@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, RNGestureHandlerDirection) {
RNGestureHandlerDirectionRight = 1,
RNGestureHandlerDirectionLeft = 2,
RNGestureHandlerDirectionUp = 4,
RNGestureHandlerDirectionDown = 8,
};

View File

@@ -0,0 +1,74 @@
#import <React/RCTEventDispatcher.h>
#import <Foundation/Foundation.h>
#import "RNGHTouchEventType.h"
#import "RNGHUIKit.h"
#import "RNGestureHandlerState.h"
@interface RNGestureHandlerEventExtraData : NSObject
@property (readonly) NSDictionary *data;
- (instancetype)initWithData:(NSDictionary *)data;
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withDuration:(NSUInteger)duration
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forPan:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withTranslation:(CGPoint)translation
withVelocity:(CGPoint)velocity
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forForce:(CGFloat)force
forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forPinch:(CGFloat)scale
withFocalPoint:(CGPoint)focalPoint
withVelocity:(CGFloat)velocity
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forRotation:(CGFloat)rotation
withAnchorPoint:(CGPoint)anchorPoint
withVelocity:(CGFloat)velocity
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forEventType:(RNGHTouchEventType)eventType
withChangedPointers:(NSArray<NSDictionary *> *)changedPointers
withAllPointers:(NSArray<NSDictionary *> *)allPointers
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType;
+ (RNGestureHandlerEventExtraData *)forPointerInside:(BOOL)pointerInside withPointerType:(NSInteger)pointerType;
@end
@interface RNGestureHandlerEvent : NSObject <RCTEvent>
- (instancetype)initWithReactTag:(NSNumber *)reactTag
handlerTag:(NSNumber *)handlerTag
state:(RNGestureHandlerState)state
extraData:(RNGestureHandlerEventExtraData *)extraData
coalescingKey:(uint16_t)coalescingKey NS_DESIGNATED_INITIALIZER;
@end
@interface RNGestureHandlerStateChange : NSObject <RCTEvent>
- (instancetype)initWithReactTag:(NSNumber *)reactTag
handlerTag:(NSNumber *)handlerTag
state:(RNGestureHandlerState)state
prevState:(RNGestureHandlerState)prevState
extraData:(RNGestureHandlerEventExtraData *)extraData NS_DESIGNATED_INITIALIZER;
@end

View File

@@ -0,0 +1,278 @@
#import "RNGestureHandlerEvents.h"
#define SAFE_VELOCITY(velocity) @(isnan(velocity) ? 0 : velocity)
@implementation RNGestureHandlerEventExtraData
- (instancetype)initWithData:(NSDictionary *)data;
{
if ((self = [super init])) {
_data = data;
}
return self;
}
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"x" : @(position.x),
@"y" : @(position.y),
@"absoluteX" : @(absolutePosition.x),
@"absoluteY" : @(absolutePosition.y),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"x" : @(position.x),
@"y" : @(position.y),
@"absoluteX" : @(absolutePosition.x),
@"absoluteY" : @(absolutePosition.y),
@"numberOfPointers" : @(numberOfTouches),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withDuration:(NSUInteger)duration
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"x" : @(position.x),
@"y" : @(position.y),
@"absoluteX" : @(absolutePosition.x),
@"absoluteY" : @(absolutePosition.y),
@"numberOfPointers" : @(numberOfTouches),
@"duration" : @(duration),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forPan:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withTranslation:(CGPoint)translation
withVelocity:(CGPoint)velocity
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"x" : @(position.x),
@"y" : @(position.y),
@"absoluteX" : @(absolutePosition.x),
@"absoluteY" : @(absolutePosition.y),
@"translationX" : @(translation.x),
@"translationY" : @(translation.y),
@"velocityX" : SAFE_VELOCITY(velocity.x),
@"velocityY" : SAFE_VELOCITY(velocity.y),
@"numberOfPointers" : @(numberOfTouches),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forForce:(CGFloat)force
forPosition:(CGPoint)position
withAbsolutePosition:(CGPoint)absolutePosition
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"x" : @(position.x),
@"y" : @(position.y),
@"absoluteX" : @(absolutePosition.x),
@"absoluteY" : @(absolutePosition.y),
@"force" : @(force),
@"numberOfPointers" : @(numberOfTouches),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forPinch:(CGFloat)scale
withFocalPoint:(CGPoint)focalPoint
withVelocity:(CGFloat)velocity
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"scale" : @(scale),
@"focalX" : @(focalPoint.x),
@"focalY" : @(focalPoint.y),
@"velocity" : SAFE_VELOCITY(velocity),
@"numberOfPointers" : @(numberOfTouches),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forRotation:(CGFloat)rotation
withAnchorPoint:(CGPoint)anchorPoint
withVelocity:(CGFloat)velocity
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"rotation" : @(rotation),
@"anchorX" : @(anchorPoint.x),
@"anchorY" : @(anchorPoint.y),
@"velocity" : SAFE_VELOCITY(velocity),
@"numberOfPointers" : @(numberOfTouches),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forEventType:(RNGHTouchEventType)eventType
withChangedPointers:(NSArray<NSDictionary *> *)changedPointers
withAllPointers:(NSArray<NSDictionary *> *)allPointers
withNumberOfTouches:(NSUInteger)numberOfTouches
withPointerType:(NSInteger)pointerType
{
if (changedPointers == nil || allPointers == nil) {
changedPointers = @[];
allPointers = @[];
eventType = RNGHTouchEventTypeUndetermined;
}
return [[RNGestureHandlerEventExtraData alloc] initWithData:@{
@"eventType" : @(eventType),
@"changedTouches" : changedPointers,
@"allTouches" : allPointers,
@"numberOfTouches" : @(numberOfTouches),
@"pointerType" : @(pointerType)
}];
}
+ (RNGestureHandlerEventExtraData *)forPointerInside:(BOOL)pointerInside withPointerType:(NSInteger)pointerType
{
return [[RNGestureHandlerEventExtraData alloc]
initWithData:@{@"pointerInside" : @(pointerInside), @"pointerType" : @(pointerType)}];
}
@end
@implementation RNGestureHandlerEvent {
NSNumber *_handlerTag;
RNGestureHandlerState _state;
RNGestureHandlerEventExtraData *_extraData;
}
@synthesize viewTag = _viewTag;
@synthesize coalescingKey = _coalescingKey;
- (instancetype)initWithReactTag:(NSNumber *)reactTag
handlerTag:(NSNumber *)handlerTag
state:(RNGestureHandlerState)state
extraData:(RNGestureHandlerEventExtraData *)extraData
coalescingKey:(uint16_t)coalescingKey
{
if ((self = [super init])) {
_viewTag = reactTag;
_handlerTag = handlerTag;
_state = state;
_extraData = extraData;
_coalescingKey = coalescingKey;
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (NSString *)eventName
{
return @"onGestureHandlerEvent";
}
- (BOOL)canCoalesce
{
return YES;
}
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent;
{
return newEvent;
}
+ (NSString *)moduleDotMethod
{
return @"RCTEventEmitter.receiveEvent";
}
- (NSArray *)arguments
{
NSMutableDictionary *body = [NSMutableDictionary dictionaryWithDictionary:_extraData.data];
[body setObject:_viewTag forKey:@"target"];
[body setObject:_handlerTag forKey:@"handlerTag"];
[body setObject:@(_state) forKey:@"state"];
return @[ self.viewTag, @"onGestureHandlerEvent", body ];
}
@end
@implementation RNGestureHandlerStateChange {
NSNumber *_handlerTag;
RNGestureHandlerState _state;
RNGestureHandlerState _prevState;
RNGestureHandlerEventExtraData *_extraData;
}
@synthesize viewTag = _viewTag;
@synthesize coalescingKey = _coalescingKey;
- (instancetype)initWithReactTag:(NSNumber *)reactTag
handlerTag:(NSNumber *)handlerTag
state:(RNGestureHandlerState)state
prevState:(RNGestureHandlerState)prevState
extraData:(RNGestureHandlerEventExtraData *)extraData
{
static uint16_t coalescingKey = 0;
if ((self = [super init])) {
_viewTag = reactTag;
_handlerTag = handlerTag;
_state = state;
_prevState = prevState;
_extraData = extraData;
_coalescingKey = coalescingKey++;
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (NSString *)eventName
{
return @"onGestureHandlerStateChange";
}
- (BOOL)canCoalesce
{
// TODO: event coalescing
return NO;
}
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent;
{
return newEvent;
}
+ (NSString *)moduleDotMethod
{
return @"RCTEventEmitter.receiveEvent";
}
- (NSArray *)arguments
{
NSMutableDictionary *body = [NSMutableDictionary dictionaryWithDictionary:_extraData.data];
[body setObject:_viewTag forKey:@"target"];
[body setObject:_handlerTag forKey:@"handlerTag"];
[body setObject:@(_state) forKey:@"state"];
[body setObject:@(_prevState) forKey:@"oldState"];
return @[ self.viewTag, @"onGestureHandlerStateChange", body ];
}
@end

View File

@@ -0,0 +1,40 @@
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "RNGestureHandler.h"
@class RCTUIManager;
@class RCTEventDispatcher;
@interface RNGestureHandlerManager : NSObject
#ifdef RCT_NEW_ARCH_ENABLED
- (nonnull instancetype)initWithModuleRegistry:(nonnull RCTModuleRegistry *)moduleRegistry
viewRegistry:(nonnull RCTViewRegistry *)viewRegistry;
#else
- (nonnull instancetype)initWithUIManager:(nonnull RCTUIManager *)uiManager
eventDispatcher:(nonnull id<RCTEventDispatcherProtocol>)eventDispatcher;
#endif // RCT_NEW_ARCH_ENABLED
- (void)createGestureHandler:(nonnull NSString *)handlerName
tag:(nonnull NSNumber *)handlerTag
config:(nonnull NSDictionary *)config;
- (void)attachGestureHandler:(nonnull NSNumber *)handlerTag
toViewWithTag:(nonnull NSNumber *)viewTag
withActionType:(RNGestureHandlerActionType)actionType;
- (void)updateGestureHandler:(nonnull NSNumber *)handlerTag config:(nonnull NSDictionary *)config;
- (void)dropGestureHandler:(nonnull NSNumber *)handlerTag;
- (void)dropAllGestureHandlers;
- (void)handleSetJSResponder:(nonnull NSNumber *)viewTag blockNativeResponder:(BOOL)blockNativeResponder;
- (void)handleClearJSResponder;
- (nullable RNGestureHandler *)handlerWithTag:(nonnull NSNumber *)handlerTag;
@end

View File

@@ -0,0 +1,428 @@
#import "RNGestureHandlerManager.h"
#import <React/RCTComponent.h>
#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTLog.h>
#import <React/RCTModalHostViewController.h>
#import <React/RCTRootContentView.h>
#import <React/RCTRootView.h>
#import <React/RCTUIManager.h>
#import <React/RCTViewManager.h>
#import "RNGestureHandler.h"
#import "RNGestureHandlerActionType.h"
#import "RNGestureHandlerRegistry.h"
#import "RNGestureHandlerState.h"
#import "RNRootViewGestureRecognizer.h"
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTFabricModalHostViewController.h>
#import <React/RCTSurfaceTouchHandler.h>
#import <React/RCTSurfaceView.h>
#import <React/RCTViewComponentView.h>
#else
#import <React/RCTTouchHandler.h>
#endif // RCT_NEW_ARCH_ENABLED
#import "Handlers/RNFlingHandler.h"
#import "Handlers/RNForceTouchHandler.h"
#import "Handlers/RNHoverHandler.h"
#import "Handlers/RNLongPressHandler.h"
#import "Handlers/RNManualHandler.h"
#import "Handlers/RNNativeViewHandler.h"
#import "Handlers/RNPanHandler.h"
#import "Handlers/RNPinchHandler.h"
#import "Handlers/RNRotationHandler.h"
#import "Handlers/RNTapHandler.h"
// We use the method below instead of RCTLog because we log out messages after the bridge gets
// turned down in some cases. Which normally with RCTLog would cause a crash in DEBUG mode
#define RCTLifecycleLog(...) \
RCTDefaultLogFunction( \
RCTLogLevelInfo, RCTLogSourceNative, @(__FILE__), @(__LINE__), [NSString stringWithFormat:__VA_ARGS__])
constexpr int NEW_ARCH_NUMBER_OF_ATTACH_RETRIES = 25;
@interface RNGestureHandlerManager () <RNGestureHandlerEventEmitter, RNRootViewGestureRecognizerDelegate>
@end
@implementation RNGestureHandlerManager {
RNGestureHandlerRegistry *_registry;
NSHashTable<RNRootViewGestureRecognizer *> *_rootViewGestureRecognizers;
NSMutableDictionary<NSNumber *, NSNumber *> *_attachRetryCounter;
#ifdef RCT_NEW_ARCH_ENABLED
RCTModuleRegistry *_moduleRegistry;
RCTViewRegistry *_viewRegistry;
#else
RCTUIManager *_uiManager;
#endif // RCT_NEW_ARCH_ENABLED
id<RCTEventDispatcherProtocol> _eventDispatcher;
id _reanimatedModule;
}
#ifdef RCT_NEW_ARCH_ENABLED
- (instancetype)initWithModuleRegistry:(RCTModuleRegistry *)moduleRegistry viewRegistry:(RCTViewRegistry *)viewRegistry
{
if ((self = [super init])) {
_moduleRegistry = moduleRegistry;
_viewRegistry = viewRegistry;
_eventDispatcher = [_moduleRegistry moduleForName:"EventDispatcher"];
[self initCommonProps];
}
return self;
}
#else
- (instancetype)initWithUIManager:(RCTUIManager *)uiManager
eventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher
{
if ((self = [super init])) {
_uiManager = uiManager;
_eventDispatcher = eventDispatcher;
[self initCommonProps];
}
return self;
}
#endif // RCT_NEW_ARCH_ENABLED
- (void)initCommonProps
{
_registry = [RNGestureHandlerRegistry new];
_rootViewGestureRecognizers = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
_attachRetryCounter = [[NSMutableDictionary alloc] init];
}
- (void)createGestureHandler:(NSString *)handlerName tag:(NSNumber *)handlerTag config:(NSDictionary *)config
{
if ([_registry handlerWithTag:handlerTag] != nullptr) {
NSString *errorMessage = [NSString
stringWithFormat:
@"Handler with tag %@ already exists. Please ensure that no Gesture instance is used across multiple GestureDetectors.",
handlerTag];
@throw [NSException exceptionWithName:@"HandlerAlreadyRegistered" reason:errorMessage userInfo:nil];
}
static NSDictionary *map;
static dispatch_once_t mapToken;
dispatch_once(&mapToken, ^{
map = @{
@"PanGestureHandler" : [RNPanGestureHandler class],
@"TapGestureHandler" : [RNTapGestureHandler class],
@"FlingGestureHandler" : [RNFlingGestureHandler class],
@"LongPressGestureHandler" : [RNLongPressGestureHandler class],
@"NativeViewGestureHandler" : [RNNativeViewGestureHandler class],
@"PinchGestureHandler" : [RNPinchGestureHandler class],
@"RotationGestureHandler" : [RNRotationGestureHandler class],
@"ForceTouchGestureHandler" : [RNForceTouchHandler class],
@"ManualGestureHandler" : [RNManualGestureHandler class],
@"HoverGestureHandler" : [RNHoverGestureHandler class],
};
});
Class nodeClass = map[handlerName];
if (!nodeClass) {
RCTLogError(@"Gesture handler type %@ is not supported", handlerName);
return;
}
RNGestureHandler *gestureHandler = [[nodeClass alloc] initWithTag:handlerTag];
[gestureHandler configure:config];
[_registry registerGestureHandler:gestureHandler];
__weak id<RNGestureHandlerEventEmitter> emitter = self;
gestureHandler.emitter = emitter;
}
- (void)attachGestureHandler:(nonnull NSNumber *)handlerTag
toViewWithTag:(nonnull NSNumber *)viewTag
withActionType:(RNGestureHandlerActionType)actionType
{
#ifdef RCT_NEW_ARCH_ENABLED
RNGHUIView *view = [_viewRegistry viewForReactTag:viewTag];
#else
RNGHUIView *view = [_uiManager viewForReactTag:viewTag];
#endif // RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
if (view == nil || view.superview == nil) {
// There are a few reasons we could end up here:
// - the native view corresponding to the viewtag hasn't yet been created
// - the native view has been created, but it's not attached to window
// - the native view will not exist because it got flattened
// In the first two cases we just want to wait until the view gets created or gets attached to its superview
// In the third case we don't want to do anything but we cannot easily distinguish it here, hece the abomination
// below
// TODO: would be great to have a better solution, although it might require migration to the shadow nodes from
// viewTags
NSNumber *counter = [_attachRetryCounter objectForKey:viewTag];
if (counter == nil) {
counter = @1;
} else {
counter = [NSNumber numberWithInt:counter.intValue + 1];
}
if (counter.intValue > NEW_ARCH_NUMBER_OF_ATTACH_RETRIES) {
[_attachRetryCounter removeObjectForKey:viewTag];
} else {
[_attachRetryCounter setObject:counter forKey:viewTag];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self attachGestureHandler:handlerTag toViewWithTag:viewTag withActionType:actionType];
});
}
return;
}
[_attachRetryCounter removeObjectForKey:viewTag];
// I think it should be moved to RNNativeViewHandler, but that would require
// additional logic for setting contentView.reactTag, this works for now
if ([view isKindOfClass:[RCTViewComponentView class]]) {
RCTViewComponentView *componentView = (RCTViewComponentView *)view;
if (componentView.contentView != nil) {
view = componentView.contentView;
}
}
view.reactTag = viewTag; // necessary for RNReanimated eventHash (e.g. "42onGestureHandlerEvent"), also will be
// returned as event.target
#endif // RCT_NEW_ARCH_ENABLED
[_registry attachHandlerWithTag:handlerTag toView:view withActionType:actionType];
// register view if not already there
[self registerViewWithGestureRecognizerAttachedIfNeeded:view];
}
- (void)updateGestureHandler:(NSNumber *)handlerTag config:(NSDictionary *)config
{
RNGestureHandler *handler = [_registry handlerWithTag:handlerTag];
[handler configure:config];
}
- (void)dropGestureHandler:(NSNumber *)handlerTag
{
[_registry dropHandlerWithTag:handlerTag];
}
- (void)dropAllGestureHandlers
{
[_registry dropAllHandlers];
}
- (void)handleSetJSResponder:(NSNumber *)viewTag blockNativeResponder:(BOOL)blockNativeResponder
{
if (blockNativeResponder) {
for (RNRootViewGestureRecognizer *recognizer in _rootViewGestureRecognizers) {
[recognizer blockOtherRecognizers];
}
}
}
- (void)handleClearJSResponder
{
// ignore...
}
- (id)handlerWithTag:(NSNumber *)handlerTag
{
return [_registry handlerWithTag:handlerTag];
}
#pragma mark Root Views Management
- (void)registerViewWithGestureRecognizerAttachedIfNeeded:(RNGHUIView *)childView
{
#ifdef RCT_NEW_ARCH_ENABLED
RNGHUIView *touchHandlerView = childView;
#if !TARGET_OS_OSX
if ([[childView reactViewController] isKindOfClass:[RCTFabricModalHostViewController class]]) {
touchHandlerView = [childView reactViewController].view;
} else {
while (touchHandlerView != nil && ![touchHandlerView isKindOfClass:[RCTSurfaceView class]]) {
touchHandlerView = touchHandlerView.superview;
}
}
#else
while (touchHandlerView != nil && ![touchHandlerView isKindOfClass:[RCTSurfaceView class]]) {
touchHandlerView = touchHandlerView.superview;
}
#endif // !TARGET_OS_OSX
#else
RNGHUIView *touchHandlerView = nil;
#if !TARGET_OS_OSX
if ([[childView reactViewController] isKindOfClass:[RCTModalHostViewController class]]) {
touchHandlerView = [childView reactViewController].view.subviews[0];
} else {
UIView *parent = childView;
while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)]) {
parent = parent.superview;
}
touchHandlerView = [[parent performSelector:@selector(touchHandler)] view];
}
#else
NSView *parent = childView;
while (parent != nil && ![parent respondsToSelector:@selector(touchHandler)]) {
parent = parent.superview;
}
touchHandlerView = [[parent performSelector:@selector(touchHandler)] view];
#endif // !TARGET_OS_OSX
#endif // RCT_NEW_ARCH_ENABLED
if (touchHandlerView == nil) {
return;
}
// Many views can return the same touchHandler so we check if the one we want to register
// is not already present in the set.
for (UIGestureRecognizer *recognizer in touchHandlerView.gestureRecognizers) {
if ([recognizer isKindOfClass:[RNRootViewGestureRecognizer class]]) {
return;
}
}
RCTLifecycleLog(@"[GESTURE HANDLER] Initialize gesture handler for view %@", touchHandlerView);
RNRootViewGestureRecognizer *recognizer = [RNRootViewGestureRecognizer new];
recognizer.delegate = self;
#if !TARGET_OS_OSX
touchHandlerView.userInteractionEnabled = YES;
#endif
[touchHandlerView addGestureRecognizer:recognizer];
[_rootViewGestureRecognizers addObject:recognizer];
}
- (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
didActivateInViewWithTouchHandler:(RNGHUIView *)viewWithTouchHandler
{
// Cancel touches in RN's root view in order to cancel all in-js recognizers
// As scroll events are special-cased in RN responder implementation and sending them would
// trigger JS responder change, we don't cancel touches if the handler that got activated is
// a scroll recognizer. This way root view will keep sending touchMove and touchEnd events
// and therefore allow JS responder to properly release the responder at the end of the touch
// stream.
// NOTE: this is not a proper fix and solving this problem requires upstream fixes to RN. In
// particular if we have one PanHandler and ScrollView that can work simultaniously then when
// the Pan handler activates it would still tigger cancel events.
// Once the upstream fix lands the line below along with this comment can be removed
#if TARGET_OS_OSX
if ([gestureRecognizer.view isKindOfClass:[NSScrollView class]]) {
return;
}
#else
if ([gestureRecognizer.view isKindOfClass:[UIScrollView class]]) {
return;
}
#endif
UIGestureRecognizer *touchHandler = nil;
// this way we can extract the touch handler on both architectures relatively easily
for (UIGestureRecognizer *recognizer in [viewWithTouchHandler gestureRecognizers]) {
#ifdef RCT_NEW_ARCH_ENABLED
if ([recognizer isKindOfClass:[RCTSurfaceTouchHandler class]]) {
#else
if ([recognizer isKindOfClass:[RCTTouchHandler class]]) {
#endif // RCT_NEW_ARCH_ENABLED
touchHandler = recognizer;
break;
}
}
[touchHandler setEnabled:NO];
[touchHandler setEnabled:YES];
}
#pragma mark Events
- (void)sendEvent:(RNGestureHandlerStateChange *)event withActionType:(RNGestureHandlerActionType)actionType
{
switch (actionType) {
case RNGestureHandlerActionTypeReanimatedWorklet:
[self sendEventForReanimated:event];
break;
case RNGestureHandlerActionTypeNativeAnimatedEvent:
if ([event.eventName isEqualToString:@"onGestureHandlerEvent"]) {
[self sendEventForNativeAnimatedEvent:event];
} else {
// Although onGestureEvent prop is an Animated.event with useNativeDriver: true,
// onHandlerStateChange prop is still a regular JS function.
// Also, Animated.event is only supported with old API.
[self sendEventForJSFunctionOldAPI:event];
}
break;
case RNGestureHandlerActionTypeJSFunctionOldAPI:
[self sendEventForJSFunctionOldAPI:event];
break;
case RNGestureHandlerActionTypeJSFunctionNewAPI:
[self sendEventForJSFunctionNewAPI:event];
break;
}
}
- (void)sendEventForReanimated:(RNGestureHandlerStateChange *)event
{
// Delivers the event to Reanimated.
#ifdef RCT_NEW_ARCH_ENABLED
// Send event directly to Reanimated
if (_reanimatedModule == nil) {
_reanimatedModule = [_moduleRegistry moduleForName:"ReanimatedModule"];
}
[_reanimatedModule eventDispatcherWillDispatchEvent:event];
#else
// In the old architecture, Reanimated overwrites RCTEventDispatcher
// with REAEventDispatcher and intercepts all direct events.
[self sendEventForDirectEvent:event];
#endif // RCT_NEW_ARCH_ENABLED
}
- (void)sendEventForNativeAnimatedEvent:(RNGestureHandlerStateChange *)event
{
// Delivers the event to NativeAnimatedModule.
// Currently, NativeAnimated[Turbo]Module is RCTEventDispatcherObserver so we can
// simply send a direct event which is handled by the observer but ignored on JS side.
// TODO: send event directly to NativeAnimated[Turbo]Module
[self sendEventForDirectEvent:event];
}
- (void)sendEventForJSFunctionOldAPI:(RNGestureHandlerStateChange *)event
{
// Delivers the event to JS (old RNGH API).
#ifdef RCT_NEW_ARCH_ENABLED
[self sendEventForDeviceEvent:event];
#else
[self sendEventForDirectEvent:event];
#endif // RCT_NEW_ARCH_ENABLED
}
- (void)sendEventForJSFunctionNewAPI:(RNGestureHandlerStateChange *)event
{
// Delivers the event to JS (new RNGH API).
[self sendEventForDeviceEvent:event];
}
- (void)sendEventForDirectEvent:(RNGestureHandlerStateChange *)event
{
// Delivers the event to JS as a direct event.
[_eventDispatcher sendEvent:event];
}
- (void)sendEventForDeviceEvent:(RNGestureHandlerStateChange *)event
{
// Delivers the event to JS as a device event.
NSMutableDictionary *body = [[event arguments] objectAtIndex:2];
[_eventDispatcher sendDeviceEventWithName:@"onGestureHandlerStateChange" body:body];
}
@end

View File

@@ -0,0 +1,19 @@
#import <React/RCTEventEmitter.h>
#import <React/RCTUIManager.h>
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTInitializing.h>
#import <rngesturehandler_codegen/rngesturehandler_codegen.h>
#else
#import <React/RCTBridgeModule.h>
#endif
@interface RNGestureHandlerModule : RCTEventEmitter
#ifdef RCT_NEW_ARCH_ENABLED
<NativeRNGestureHandlerModuleSpec, RCTJSDispatcherModule, RCTInitializing>
#else
<RCTBridgeModule>
#endif
@end

View File

@@ -0,0 +1,320 @@
#import "RNGestureHandlerModule.h"
#import <React/RCTComponent.h>
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <React/RCTUIManagerObserverCoordinator.h>
#import <React/RCTUIManagerUtils.h>
#import <React/RCTViewManager.h>
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTBridge+Private.h>
#import <React/RCTBridge.h>
#import <React/RCTUtils.h>
#import <ReactCommon/CallInvoker.h>
#import <ReactCommon/RCTTurboModule.h>
#import <react/renderer/uimanager/primitives.h>
#endif // RCT_NEW_ARCH_ENABLED
#import "RNGestureHandler.h"
#import "RNGestureHandlerDirection.h"
#import "RNGestureHandlerManager.h"
#import "RNGestureHandlerState.h"
#import "RNGestureHandlerButton.h"
#import "RNGestureHandlerStateManager.h"
#import <React/RCTJSThread.h>
#ifdef RCT_NEW_ARCH_ENABLED
using namespace facebook;
using namespace react;
#endif // RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
@interface RNGestureHandlerModule () <RNGestureHandlerStateManager, RCTTurboModule>
@end
#else
@interface RNGestureHandlerModule () <RCTUIManagerObserver, RNGestureHandlerStateManager>
@end
#endif // RCT_NEW_ARCH_ENABLED
typedef void (^GestureHandlerOperation)(RNGestureHandlerManager *manager);
@implementation RNGestureHandlerModule {
RNGestureHandlerManager *_manager;
// Oparations called after views have been updated.
NSMutableArray<GestureHandlerOperation> *_operations;
}
#ifdef RCT_NEW_ARCH_ENABLED
@synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED;
@synthesize bridge = _bridge;
@synthesize dispatchToJSThread = _dispatchToJSThread;
#endif // RCT_NEW_ARCH_ENABLED
RCT_EXPORT_MODULE()
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (void)invalidate
{
RNGestureHandlerManager *handlerManager = _manager;
dispatch_async(dispatch_get_main_queue(), ^{
[handlerManager dropAllGestureHandlers];
});
_manager = nil;
#ifndef RCT_NEW_ARCH_ENABLED
[self.bridge.uiManager.observerCoordinator removeObserver:self];
#endif // RCT_NEW_ARCH_ENABLED
}
- (dispatch_queue_t)methodQueue
{
// This module needs to be on the same queue as the UIManager to avoid
// having to lock `_operations` and `_preOperations` since `uiManagerWillFlushUIBlocks`
// will be called from that queue.
// This is required as this module rely on having all the view nodes created before
// gesture handlers can be associated with them
return RCTGetUIManagerQueue();
}
#ifdef RCT_NEW_ARCH_ENABLED
void decorateRuntime(jsi::Runtime &runtime)
{
auto isFormsStackingContext = jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "isFormsStackingContext"),
1,
[](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count) -> jsi::Value {
if (!arguments[0].isObject()) {
return jsi::Value::null();
}
auto shadowNode = arguments[0].asObject(runtime).getNativeState<ShadowNode>(runtime);
bool isFormsStackingContext = shadowNode->getTraits().check(ShadowNodeTraits::FormsStackingContext);
return jsi::Value(isFormsStackingContext);
});
runtime.global().setProperty(runtime, "isFormsStackingContext", std::move(isFormsStackingContext));
}
#endif // RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
- (void)initialize
{
_manager = [[RNGestureHandlerManager alloc] initWithModuleRegistry:self.moduleRegistry
viewRegistry:_viewRegistry_DEPRECATED];
_operations = [NSMutableArray new];
}
#else
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
_manager = [[RNGestureHandlerManager alloc] initWithUIManager:bridge.uiManager
eventDispatcher:bridge.eventDispatcher];
_operations = [NSMutableArray new];
[bridge.uiManager.observerCoordinator addObserver:self];
}
#endif // RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
{
dispatch_block_t block = ^{
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)[RCTBridge currentBridge];
auto runtime = (jsi::Runtime *)cxxBridge.runtime;
decorateRuntime(*runtime);
};
if (_dispatchToJSThread) {
_dispatchToJSThread(block);
} else {
[[RCTBridge currentBridge] dispatchBlock:block queue:RCTJSThread];
}
return @true;
}
#endif // RCT_NEW_ARCH_ENABLED
RCT_EXPORT_METHOD(createGestureHandler
: (nonnull NSString *)handlerName handlerTag
: (double)handlerTag config
: (NSDictionary *)config)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager createGestureHandler:handlerName tag:[NSNumber numberWithDouble:handlerTag] config:config];
}];
}
RCT_EXPORT_METHOD(attachGestureHandler : (double)handlerTag newView : (double)viewTag actionType : (double)actionType)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager attachGestureHandler:[NSNumber numberWithDouble:handlerTag]
toViewWithTag:[NSNumber numberWithDouble:viewTag]
withActionType:(RNGestureHandlerActionType)[[NSNumber numberWithDouble:actionType] integerValue]];
}];
}
RCT_EXPORT_METHOD(updateGestureHandler : (double)handlerTag newConfig : (NSDictionary *)config)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager updateGestureHandler:[NSNumber numberWithDouble:handlerTag] config:config];
}];
}
RCT_EXPORT_METHOD(dropGestureHandler : (double)handlerTag)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager dropGestureHandler:[NSNumber numberWithDouble:handlerTag]];
}];
}
RCT_EXPORT_METHOD(handleSetJSResponder : (double)viewTag blockNativeResponder : (BOOL)blockNativeResponder)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager handleSetJSResponder:[NSNumber numberWithDouble:viewTag] blockNativeResponder:blockNativeResponder];
}];
}
RCT_EXPORT_METHOD(handleClearJSResponder)
{
[self addOperationBlock:^(RNGestureHandlerManager *manager) {
[manager handleClearJSResponder];
}];
}
RCT_EXPORT_METHOD(flushOperations)
{
// On the new arch we rely on `flushOperations` for scheduling the operations on the UI thread.
// On the old arch we rely on `uiManagerWillPerformMounting`
#ifdef RCT_NEW_ARCH_ENABLED
if (_operations.count == 0) {
return;
}
NSArray<GestureHandlerOperation> *operations = _operations;
_operations = [NSMutableArray new];
[self.viewRegistry_DEPRECATED addUIBlock:^(RCTViewRegistry *viewRegistry) {
for (GestureHandlerOperation operation in operations) {
operation(self->_manager);
}
}];
#endif // RCT_NEW_ARCH_ENABLED
}
- (void)setGestureState:(int)state forHandler:(int)handlerTag
{
RNGestureHandler *handler = [_manager handlerWithTag:@(handlerTag)];
if (handler != nil) {
if (state == 1) { // FAILED
handler.recognizer.state = RNGHGestureRecognizerStateFailed;
} else if (state == 2) { // BEGAN
handler.recognizer.state = RNGHGestureRecognizerStatePossible;
} else if (state == 3) { // CANCELLED
handler.recognizer.state = RNGHGestureRecognizerStateCancelled;
} else if (state == 4) { // ACTIVE
[handler stopActivationBlocker];
handler.recognizer.state = RNGHGestureRecognizerStateBegan;
} else if (state == 5) { // ENDED
handler.recognizer.state = RNGHGestureRecognizerStateEnded;
}
}
// if the gesture was set to finish, cancel all pointers it was tracking
if (state == 1 || state == 3 || state == 5) {
[handler.pointerTracker cancelPointers];
}
// do not send state change event when activating because it bypasses
// shouldRequireFailureOfGestureRecognizer
if (state != 4) {
[handler handleGesture:handler.recognizer];
}
}
#pragma mark-- Batch handling
- (void)addOperationBlock:(GestureHandlerOperation)operation
{
[_operations addObject:operation];
}
#ifndef RCT_NEW_ARCH_ENABLED
#pragma mark - RCTUIManagerObserver
- (void)uiManagerWillFlushUIBlocks:(RCTUIManager *)uiManager
{
[self uiManagerWillPerformMounting:uiManager];
}
- (void)uiManagerWillPerformMounting:(RCTUIManager *)uiManager
{
if (_operations.count == 0) {
return;
}
NSArray<GestureHandlerOperation> *operations = _operations;
_operations = [NSMutableArray new];
[uiManager
addUIBlock:^(__unused RCTUIManager *manager, __unused NSDictionary<NSNumber *, RNGHUIView *> *viewRegistry) {
for (GestureHandlerOperation operation in operations) {
operation(self->_manager);
}
}];
}
#endif // RCT_NEW_ARCH_ENABLED
#pragma mark Events
- (NSArray<NSString *> *)supportedEvents
{
return @[ @"onGestureHandlerEvent", @"onGestureHandlerStateChange" ];
}
#pragma mark Module Constants
- (NSDictionary *)constantsToExport
{
return @{
@"State" : @{
@"UNDETERMINED" : @(RNGestureHandlerStateUndetermined),
@"BEGAN" : @(RNGestureHandlerStateBegan),
@"ACTIVE" : @(RNGestureHandlerStateActive),
@"CANCELLED" : @(RNGestureHandlerStateCancelled),
@"FAILED" : @(RNGestureHandlerStateFailed),
@"END" : @(RNGestureHandlerStateEnd)
},
@"Direction" : @{
@"RIGHT" : @(RNGestureHandlerDirectionRight),
@"LEFT" : @(RNGestureHandlerDirectionLeft),
@"UP" : @(RNGestureHandlerDirectionUp),
@"DOWN" : @(RNGestureHandlerDirectionDown)
}
};
}
#if RCT_NEW_ARCH_ENABLED
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeRNGestureHandlerModuleSpecJSI>(params);
}
#endif
@end

View File

@@ -0,0 +1,25 @@
#import <Foundation/Foundation.h>
#import "RNGHTouchEventType.h"
#import "RNGHUIKit.h"
#define MAX_POINTERS_COUNT 12
@class RNGestureHandler;
@interface RNGestureHandlerPointerTracker : NSObject
@property (nonatomic) RNGHTouchEventType eventType;
@property (nonatomic) NSArray<NSDictionary *> *changedPointersData;
@property (nonatomic) NSArray<NSDictionary *> *allPointersData;
@property (nonatomic) int trackedPointersCount;
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event;
- (void)reset;
- (void)cancelPointers;
@end

View File

@@ -0,0 +1,248 @@
#import "RNGestureHandlerPointerTracker.h"
#import "RNGestureHandler.h"
#import <React/UIView+React.h>
@implementation RNGestureHandlerPointerTracker {
__weak RNGestureHandler *_gestureHandler;
RNGHUITouch *_trackedPointers[MAX_POINTERS_COUNT];
int _trackedPointersCount;
}
- (id)initWithGestureHandler:(id)gestureHandler
{
_gestureHandler = gestureHandler;
_trackedPointersCount = 0;
_changedPointersData = nil;
_allPointersData = nil;
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
_trackedPointers[i] = nil;
}
return self;
}
- (int)registerTouch:(RNGHUITouch *)touch
{
for (int index = 0; index < MAX_POINTERS_COUNT; index++) {
if (_trackedPointers[index] == nil) {
_trackedPointers[index] = touch;
return index;
}
}
return -1;
}
- (int)unregisterTouch:(RNGHUITouch *)touch
{
for (int index = 0; index < MAX_POINTERS_COUNT; index++) {
if (_trackedPointers[index] == touch) {
_trackedPointers[index] = nil;
return index;
}
}
return -1;
}
- (int)findTouchIndex:(RNGHUITouch *)touch
{
for (int index = 0; index < MAX_POINTERS_COUNT; index++) {
if (_trackedPointers[index] == touch) {
return index;
}
}
return -1;
}
- (int)registeredTouchesCount
{
int count = 0;
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
if (_trackedPointers[i] != nil) {
count++;
}
}
return count;
}
- (NSDictionary *)extractPointerData:(int)index forTouch:(RNGHUITouch *)touch
{
#if TARGET_OS_OSX
CGPoint absolutePos = [touch locationInWindow];
CGPoint relativePos = [touch.window.contentView convertPoint:absolutePos fromView:_gestureHandler.recognizer.view];
#else
CGPoint relativePos = [touch locationInView:_gestureHandler.recognizer.view];
CGPoint absolutePos = [touch locationInView:_gestureHandler.recognizer.view.window];
#endif
return @{
@"id" : @(index),
@"x" : @(relativePos.x),
@"y" : @(relativePos.y),
@"absoluteX" : @(absolutePos.x),
@"absoluteY" : @(absolutePos.y)
};
}
- (void)extractAllTouches
{
int registeredTouches = [self registeredTouchesCount];
NSDictionary *data[registeredTouches];
int nextIndex = 0;
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
RNGHUITouch *touch = _trackedPointers[i];
if (touch != nil) {
data[nextIndex++] = [self extractPointerData:i forTouch:touch];
}
}
_allPointersData = [[NSArray alloc] initWithObjects:data count:registeredTouches];
}
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
_eventType = RNGHTouchEventTypePointerDown;
NSDictionary *data[touches.count];
for (int i = 0; i < [touches count]; i++) {
RNGHUITouch *touch = [[touches allObjects] objectAtIndex:i];
int index = [self registerTouch:touch];
if (index >= 0) {
_trackedPointersCount++;
}
data[i] = [self extractPointerData:index forTouch:touch];
}
_changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]];
// extract all touches last to include the ones that were just added
[self extractAllTouches];
[self sendEvent];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
_eventType = RNGHTouchEventTypePointerMove;
NSDictionary *data[touches.count];
for (int i = 0; i < [touches count]; i++) {
RNGHUITouch *touch = [[touches allObjects] objectAtIndex:i];
int index = [self findTouchIndex:touch];
data[i] = [self extractPointerData:index forTouch:touch];
}
_changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]];
[self extractAllTouches];
[self sendEvent];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
// extract all touches first to include the ones that were just lifted
[self extractAllTouches];
_eventType = RNGHTouchEventTypePointerUp;
NSDictionary *data[touches.count];
for (int i = 0; i < [touches count]; i++) {
RNGHUITouch *touch = [[touches allObjects] objectAtIndex:i];
int index = [self unregisterTouch:touch];
if (index >= 0) {
_trackedPointersCount--;
}
data[i] = [self extractPointerData:index forTouch:touch];
}
_changedPointersData = [[NSArray alloc] initWithObjects:data count:[touches count]];
[self sendEvent];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
if (!_gestureHandler.needsPointerData) {
return;
}
[self reset];
}
- (void)reset
{
if (!_gestureHandler.needsPointerData) {
return;
}
if (_trackedPointersCount == 0) {
// gesture has finished because all pointers were lifted, reset event type to send state change event
_eventType = RNGHTouchEventTypeUndetermined;
} else {
// turns out that the gesture may be made to fail without calling touchesCancelled in that case there
// are still tracked pointers but the recognizer state is already set to UIGestureRecognizerStateFailed
// we need to clear the pointers and send info about their cancellation
[self cancelPointers];
}
[_gestureHandler reset];
}
- (void)cancelPointers
{
// extract all touches first to include the ones that were just cancelled
[self extractAllTouches];
int registeredTouches = [self registeredTouchesCount];
if (registeredTouches > 0) {
int nextIndex = 0;
NSDictionary *data[registeredTouches];
for (int i = 0; i < MAX_POINTERS_COUNT; i++) {
RNGHUITouch *touch = _trackedPointers[i];
if (touch != nil) {
data[nextIndex++] = [self extractPointerData:i forTouch:touch];
[self unregisterTouch:touch];
}
}
_eventType = RNGHTouchEventTypeCancelled;
_changedPointersData = [[NSArray alloc] initWithObjects:data count:registeredTouches];
[self sendEvent];
_trackedPointersCount = 0;
}
}
- (void)sendEvent
{
// it may happen that the gesture recognizer is reset after it's been unbound from the view,
// it that recognizer tried to send event, the app would crash because the target of the event
// would be nil.
if (!_gestureHandler.needsPointerData || _gestureHandler.recognizer.view.reactTag == nil) {
return;
}
[_gestureHandler sendTouchEventInState:[_gestureHandler state]
forViewWithTag:_gestureHandler.recognizer.view.reactTag];
}
@end

View File

@@ -0,0 +1,8 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, RNGestureHandlerPointerType) {
RNGestureHandlerTouch = 0,
RNGestureHandlerStylus,
RNGestureHandlerMouse,
RNGestureHandlerOtherPointer,
};

View File

@@ -0,0 +1,21 @@
//
// RNGestureHandlerRegistry.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNGestureHandlerRegistry : NSObject
- (nullable RNGestureHandler *)handlerWithTag:(nonnull NSNumber *)handlerTag;
- (void)registerGestureHandler:(nonnull RNGestureHandler *)gestureHandler;
- (void)attachHandlerWithTag:(nonnull NSNumber *)handlerTag
toView:(nonnull RNGHUIView *)view
withActionType:(RNGestureHandlerActionType)actionType;
- (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag;
- (void)dropAllHandlers;
@end

View File

@@ -0,0 +1,63 @@
//
// RNGestureHandlerRegistry.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandlerRegistry.h"
#import <React/RCTAssert.h>
@implementation RNGestureHandlerRegistry {
NSMutableDictionary<NSNumber *, RNGestureHandler *> *_handlers;
}
- (instancetype)init
{
if ((self = [super init])) {
_handlers = [NSMutableDictionary new];
}
return self;
}
- (RNGestureHandler *)handlerWithTag:(NSNumber *)handlerTag
{
return _handlers[handlerTag];
}
- (void)registerGestureHandler:(RNGestureHandler *)gestureHandler
{
_handlers[gestureHandler.tag] = gestureHandler;
}
- (void)attachHandlerWithTag:(NSNumber *)handlerTag
toView:(RNGHUIView *)view
withActionType:(RNGestureHandlerActionType)actionType
{
RNGestureHandler *handler = _handlers[handlerTag];
RCTAssert(handler != nil, @"Handler for tag %@ does not exists", handlerTag);
[handler unbindFromView];
handler.actionType = actionType;
[handler bindToView:view];
}
- (void)dropHandlerWithTag:(NSNumber *)handlerTag
{
RNGestureHandler *handler = _handlers[handlerTag];
[handler unbindFromView];
[_handlers removeObjectForKey:handlerTag];
}
- (void)dropAllHandlers
{
for (NSNumber *tag in _handlers) {
RNGestureHandler *handler = _handlers[tag];
[handler unbindFromView];
}
[_handlers removeAllObjects];
}
@end

View File

@@ -0,0 +1,21 @@
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTFabricComponentsPlugins.h>
Class<RCTComponentViewProtocol> RNGestureHandlerRootViewCls(void)
{
// RNGestureHandlerRootView is Android-only.
// However, if we set `excludedPlatforms: ['iOS']` in `codegenNativeComponent`,
// codegen still generates `RNGestureHandlerRootViewShadowNode` (which it shouldn't)
// and thus the project fails to compile due to missing `RNGestureHandlerRootViewProps`.
// As a workaround, we could set `interfaceOnly: true` to disable autogenerating ShadowNode
// but then we would have to implement ComponentDescriptor and ShadowNode ourselves
// (probably just generate it once and keep the generated files in the repo)
// as well as make additional configuration so these files are actually compiled for Android.
// This simple trick allows us to have all the necessary files generated
// and compiled on Android while avoiding compilation errors on iOS.
// TODO: remove this file once `excludedPlatforms` option properly disables generating ShadowNode
return nil;
}
#endif // RCT_NEW_ARCH_ENABLED

View File

@@ -0,0 +1,10 @@
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, RNGestureHandlerState) {
RNGestureHandlerStateUndetermined = 0,
RNGestureHandlerStateFailed,
RNGestureHandlerStateBegan,
RNGestureHandlerStateCancelled,
RNGestureHandlerStateActive,
RNGestureHandlerStateEnd,
};

View File

@@ -0,0 +1,5 @@
@protocol RNGestureHandlerStateManager
- (void)setGestureState:(int)state forHandler:(int)handlerTag;
@end

View File

@@ -0,0 +1,18 @@
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#else
#import <Appkit/NSGestureRecognizer.h>
#endif
@class RNGestureHandler;
#if !TARGET_OS_OSX
@interface RNManualActivationRecognizer : UIGestureRecognizer <UIGestureRecognizerDelegate>
#else
@interface RNManualActivationRecognizer : NSGestureRecognizer <NSGestureRecognizerDelegate>
#endif
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler;
- (void)fail;
@end

View File

@@ -0,0 +1,108 @@
#import "RNManualActivationRecognizer.h"
#import "RNGestureHandler.h"
@implementation RNManualActivationRecognizer {
RNGestureHandler *_handler;
int _activePointers;
}
- (id)initWithGestureHandler:(RNGestureHandler *)gestureHandler
{
if ((self = [super initWithTarget:self action:@selector(handleGesture:)])) {
_handler = gestureHandler;
_activePointers = 0;
self.delegate = self;
#if !TARGET_OS_OSX
self.cancelsTouchesInView = NO;
#endif
}
return self;
}
- (void)handleGesture:(UIGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
self.state = UIGestureRecognizerStateEnded;
[self reset];
}
}
#if TARGET_OS_OSX
- (void)mouseUp:(NSEvent *)event
{
[super mouseUp:event];
_activePointers -= 1;
}
- (void)mouseDown:(NSEvent *)event
{
[super mouseDown:event];
_activePointers += 1;
if (_activePointers == 0) {
self.state = UIGestureRecognizerStateBegan;
}
}
#else
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
_activePointers += touches.count;
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
_activePointers -= touches.count;
if (_activePointers == 0) {
self.state = UIGestureRecognizerStateBegan;
}
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
_activePointers = 0;
[self reset];
}
#endif
- (void)reset
{
self.enabled = YES;
[super reset];
}
- (void)fail
{
self.enabled = NO;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
if (handler != nil) {
if (handler.tag == _handler.tag) {
return YES;
}
}
return NO;
}
@end

View File

@@ -0,0 +1,17 @@
//
// RNRootViewGestureRecognizer.h
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNGestureHandler.h"
@interface RNRootViewGestureRecognizer : UIGestureRecognizer
@property (nullable, nonatomic, weak) id<RNRootViewGestureRecognizerDelegate> delegate;
- (void)blockOtherRecognizers;
@end

View File

@@ -0,0 +1,174 @@
//
// RNRootViewGestureRecognizer.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNRootViewGestureRecognizer.h"
#if !TARGET_OS_OSX
#import <UIKit/UIGestureRecognizerSubclass.h>
#endif
#ifdef RCT_NEW_ARCH_ENABLED
#import <React/RCTSurfaceTouchHandler.h>
#else
#import <React/RCTTouchHandler.h>
#endif // RCT_NEW_ARCH_ENABLED
@implementation RNRootViewGestureRecognizer {
BOOL _active;
}
@dynamic delegate;
- (instancetype)init
{
#if !TARGET_OS_OSX
if (self = [super init]) {
self.delaysTouchesEnded = NO;
self.delaysTouchesBegan = NO;
}
#else
self = [super init];
#endif
return self;
}
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// This method is used to implement "enabled" feature for gesture handlers. We enforce gesture
// recognizers that are connected with "disabled" handlers to wait for the root gesture
// recognizer to fail and this way we block them from acting.
RNGestureHandler *otherHandler = [RNGestureHandler findGestureHandlerByRecognizer:otherGestureRecognizer];
if (otherHandler != nil && otherHandler.enabled == NO) {
return YES;
}
return NO;
}
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
{
return ![preventedGestureRecognizer isKindOfClass:[
#ifdef RCT_NEW_ARCH_ENABLED
RCTSurfaceTouchHandler
#else
RCTTouchHandler
#endif
class]];
}
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
{
// When this method is called it means that one of handlers has activated, in this case we want
// to send an info to JS so that it cancells all JS responders, as long as the preventing
// recognizer is from Gesture Handler, otherwise we might break some interactions
RNGestureHandler *handler = [RNGestureHandler findGestureHandlerByRecognizer:preventingGestureRecognizer];
if (handler != nil) {
[self.delegate gestureRecognizer:preventingGestureRecognizer didActivateInViewWithTouchHandler:self.view];
}
return [super canBePreventedByGestureRecognizer:preventingGestureRecognizer];
}
- (void)interactionsBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_active = YES;
self.state = UIGestureRecognizerStatePossible;
}
- (void)interactionsMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStatePossible;
}
- (void)interactionsEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.state == UIGestureRecognizerStateBegan || self.state == UIGestureRecognizerStateChanged) {
self.state = UIGestureRecognizerStateEnded;
} else {
self.state = UIGestureRecognizerStateFailed;
}
[self reset];
_active = NO;
}
- (void)interactionsCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStateCancelled;
[self reset];
_active = NO;
}
#if TARGET_OS_OSX
- (void)mouseDown:(NSEvent *)event
{
[super mouseDown:event];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
}
- (void)rightMouseDown:(NSEvent *)event
{
[super rightMouseDown:event];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
}
- (void)mouseDragged:(NSEvent *)event
{
[super mouseDragged:event];
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
}
- (void)rightMouseDragged:(NSEvent *)event
{
[super rightMouseDragged:event];
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
}
- (void)mouseUp:(NSEvent *)event
{
[super mouseUp:event];
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
}
- (void)rightMouseUp:(NSEvent *)event
{
[super rightMouseUp:event];
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
}
#else
- (void)touchesBegan:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[self interactionsBegan:[NSSet setWithObject:event] withEvent:event];
}
- (void)touchesMoved:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self interactionsMoved:[NSSet setWithObject:event] withEvent:event];
}
- (void)touchesEnded:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self interactionsEnded:[NSSet setWithObject:event] withEvent:event];
}
- (void)touchesCancelled:(NSSet<RNGHUITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self interactionsCancelled:[NSSet setWithObject:event] withEvent:event];
}
#endif
- (void)blockOtherRecognizers
{
if (_active) {
self.state = UIGestureRecognizerStateBegan;
}
}
@end