This commit is contained in:
Yamozha
2021-04-02 02:24:13 +03:00
parent c23950b545
commit 7256d79e2c
31493 changed files with 3036630 additions and 0 deletions

View File

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

View File

@ -0,0 +1,33 @@
#import "RNFlingHandler.h"
@implementation RNFlingGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
}
return self;
}
- (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
}
@end

View File

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

View File

@ -0,0 +1,148 @@
#import "RNForceTouchHandler.h"
#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;
UITouch *_firstTouch;
}
- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_force = 0;
_minForce = 0.2;
_maxForce = NAN;
_feedbackOnActivation = NO;
}
return self;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (_firstTouch) {
// ignore rest of fingers
return;
}
[super touchesBegan:touches withEvent:event];
_firstTouch = [touches anyObject];
[self handleForceWithTouches:touches];
self.state = UIGestureRecognizerStatePossible;
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (![touches containsObject:_firstTouch]) {
// Considered only the very first touch
return;
}
[super 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
if (_feedbackOnActivation) {
if (@available(iOS 10.0, *)) {
[[[UIImpactFeedbackGenerator alloc] initWithStyle:(UIImpactFeedbackStyleMedium)] impactOccurred];
}
}
#endif
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (![touches containsObject:_firstTouch]) {
// Considered only the very first touch
return;
}
[super touchesEnded:touches withEvent:event];
if (self.state == UIGestureRecognizerStateBegan || self.state == UIGestureRecognizerStateChanged) {
self.state = UIGestureRecognizerStateEnded;
} else {
self.state = UIGestureRecognizerStateFailed;
}
}
- (void)handleForceWithTouches:(NSSet<UITouch *> *)touches {
_force = _firstTouch.force / _firstTouch.maximumPossibleForce;
}
- (void)reset {
[super 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)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];
}
@end

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,83 @@
//
// RNLongPressHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNLongPressHandler.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <React/RCTConvert.h>
@interface RNBetterLongPressGestureRecognizer : UILongPressGestureRecognizer
- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler;
@end
@implementation RNBetterLongPressGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
}
- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
}
return self;
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if (_gestureHandler.shouldCancelWhenOutside && ![_gestureHandler containsPointInView]) {
self.enabled = NO;
self.enabled = YES;
}
}
@end
@implementation RNLongPressGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNBetterLongPressGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (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];
}
@end

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,149 @@
//
// RNNativeViewHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNNativeViewHandler.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#import <React/RCTConvert.h>
#import <React/RCTScrollView.h>
#import <React/UIView+React.h>
#pragma mark RNDummyGestureRecognizer
@implementation RNDummyGestureRecognizer
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStateFailed;
[self reset];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStateCancelled;
[self reset];
}
@end
#pragma mark RNNativeViewgestureHandler
@implementation RNNativeViewGestureHandler {
BOOL _shouldActivateOnStart;
BOOL _disallowInterruption;
}
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNDummyGestureRecognizer alloc] init];
}
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
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;
}
}
- (void)handleTouchDown:(UIView *)sender forEvent:(UIEvent *)event
{
[self reset];
if (_disallowInterruption) {
// When `disallowInterruption` is set we cancel all gesture handlers when this UIControl
// gets DOWN event
for (UITouch *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]];
}
- (void)handleTouchUpOutside:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateEnd
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO]];
}
- (void)handleTouchUpInside:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateEnd
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES]];
}
- (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]];
} else {
[self sendEventsInState:RNGestureHandlerStateActive
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO]];
}
}
- (void)handleDragEnter:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateActive
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES]];
}
- (void)handleTouchCancel:(UIView *)sender forEvent:(UIEvent *)event
{
[self sendEventsInState:RNGestureHandlerStateCancelled
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:NO]];
}
@end

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,239 @@
//
// RNPanHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNPanHandler.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface RNBetterPanGestureRecognizer : UIPanGestureRecognizer
@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;
- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler;
@end
@implementation RNBetterPanGestureRecognizer {
__weak RNGestureHandler *_gestureHandler;
NSUInteger _realMinimumNumberOfTouches;
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;
_hasCustomActivationCriteria = NO;
#if !TARGET_OS_TV
_realMinimumNumberOfTouches = self.minimumNumberOfTouches;
#endif
}
return self;
}
- (void)setMinimumNumberOfTouches:(NSUInteger)minimumNumberOfTouches
{
_realMinimumNumberOfTouches = minimumNumberOfTouches;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
#if !TARGET_OS_TV
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
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStatePossible && [self shouldFailUnderCustomCriteria]) {
self.state = UIGestureRecognizerStateFailed;
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 (_hasCustomActivationCriteria && self.state == UIGestureRecognizerStatePossible && [self shouldActivateUnderCustomCriteria]) {
#if !TARGET_OS_TV
super.minimumNumberOfTouches = _realMinimumNumberOfTouches;
if ([self numberOfTouches] >= _realMinimumNumberOfTouches) {
self.state = UIGestureRecognizerStateBegan;
[self setTranslation:CGPointMake(0, 0) inView:self.view];
}
#endif
}
}
- (void)reset
{
self.enabled = YES;
[super reset];
}
- (void)updateHasCustomActivationCriteria
{
_hasCustomActivationCriteria = !isnan(_minDistSq)
|| !isnan(_minVelocityX) || !isnan(_minVelocityY) || !isnan(_minVelocitySq)
|| !isnan(_activeOffsetXStart) || !isnan(_activeOffsetXEnd)
|| !isnan(_activeOffsetYStart) || !isnan(_activeOffsetYEnd);
}
- (BOOL)shouldFailUnderCustomCriteria
{
CGPoint trans = [self translationInView:self.view];
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
{
CGPoint trans = [self translationInView:self.view];
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)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_TV
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;
}
[recognizer updateHasCustomActivationCriteria];
}
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIPanGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData
forPan:[recognizer locationInView:recognizer.view]
withAbsolutePosition:[recognizer locationInView:recognizer.view.window]
withTranslation:[recognizer translationInView:recognizer.view]
withVelocity:[recognizer velocityInView:recognizer.view.window]
withNumberOfTouches:recognizer.numberOfTouches];
}
@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,35 @@
//
// RNPinchHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNPinchHandler.h"
@implementation RNPinchGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
#if !TARGET_OS_TV
_recognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
#endif
}
return self;
}
#if !TARGET_OS_TV
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIPinchGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData
forPinch:recognizer.scale
withFocalPoint:[recognizer locationInView:recognizer.view]
withVelocity:recognizer.velocity
withNumberOfTouches:recognizer.numberOfTouches];
}
#endif
@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,35 @@
//
// RNRotationHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNRotationHandler.h"
@implementation RNRotationGestureHandler
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
#if !TARGET_OS_TV
_recognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
#endif
}
return self;
}
#if !TARGET_OS_TV
- (RNGestureHandlerEventExtraData *)eventExtraData:(UIRotationGestureRecognizer *)recognizer
{
return [RNGestureHandlerEventExtraData
forRotation:recognizer.rotation
withAnchorPoint:[recognizer locationInView:recognizer.view]
withVelocity:recognizer.velocity
withNumberOfTouches:recognizer.numberOfTouches];
}
#endif
@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,218 @@
//
// RNTapHandler.m
// RNGestureHandler
//
// Created by Krzysztof Magiera on 12/10/2017.
// Copyright © 2017 Software Mansion. All rights reserved.
//
#import "RNTapHandler.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#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;
}
- (id)initWithGestureHandler:(RNGestureHandler*)gestureHandler
{
if ((self = [super initWithTarget:gestureHandler action:@selector(handleGesture:)])) {
_gestureHandler = gestureHandler;
_tapsSoFar = 0;
_numberOfTaps = 1;
_minPointers = 1;
_maxDelay = 0.2;
_maxDuration = NAN;
_maxDeltaX = NAN;
_maxDeltaY = NAN;
_maxDistSq = NAN;
}
return self;
}
- (void)triggerAction
{
[_gestureHandler handleGesture:self];
}
- (void)cancel
{
self.enabled = NO;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
if (_tapsSoFar == 0) {
_initPosition = [self locationInView:self.view];
}
_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)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super 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];
}
- (CGPoint)translationInView {
CGPoint currentPosition = [self locationInView:self.view];
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)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
if (_numberOfTaps == _tapsSoFar && _maxNumberOfTouches >= _minPointers) {
self.state = UIGestureRecognizerStateEnded;
[self reset];
} else {
[self performSelector:@selector(cancel) withObject:nil afterDelay:_maxDelay];
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
self.state = UIGestureRecognizerStateCancelled;
[self reset];
}
- (void)reset
{
if (self.state == UIGestureRecognizerStateFailed) {
[self triggerAction];
}
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil];
_tapsSoFar = 0;
_maxNumberOfTouches = 0;
self.enabled = YES;
[super reset];
}
@end
@implementation RNTapGestureHandler {
RNGestureHandlerEventExtraData * _lastData;
}
- (instancetype)initWithTag:(NSNumber *)tag
{
if ((self = [super initWithTag:tag])) {
_recognizer = [[RNBetterTapGestureRecognizer alloc] initWithGestureHandler:self];
}
return self;
}
- (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;
}
@end