194 lines
6.7 KiB
Objective-C
194 lines
6.7 KiB
Objective-C
#import "RNGestureHandlerManager.h"
|
|
|
|
#import <React/RCTLog.h>
|
|
#import <React/RCTViewManager.h>
|
|
#import <React/RCTComponent.h>
|
|
#import <React/RCTRootView.h>
|
|
#import <React/RCTTouchHandler.h>
|
|
#import <React/RCTUIManager.h>
|
|
#import <React/RCTEventDispatcher.h>
|
|
|
|
#if __has_include(<React/RCTRootContentView.h>)
|
|
#import <React/RCTRootContentView.h>
|
|
#else
|
|
#import "RCTRootContentView.h"
|
|
#endif
|
|
|
|
#import "RNGestureHandlerState.h"
|
|
#import "RNGestureHandler.h"
|
|
#import "RNGestureHandlerRegistry.h"
|
|
#import "RNRootViewGestureRecognizer.h"
|
|
|
|
#import "Handlers/RNPanHandler.h"
|
|
#import "Handlers/RNTapHandler.h"
|
|
#import "Handlers/RNFlingHandler.h"
|
|
#import "Handlers/RNLongPressHandler.h"
|
|
#import "Handlers/RNNativeViewHandler.h"
|
|
#import "Handlers/RNPinchHandler.h"
|
|
#import "Handlers/RNRotationHandler.h"
|
|
#import "Handlers/RNForceTouchHandler.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__])
|
|
|
|
@interface RNGestureHandlerManager () <RNGestureHandlerEventEmitter, RNRootViewGestureRecognizerDelegate>
|
|
|
|
@end
|
|
|
|
@implementation RNGestureHandlerManager
|
|
{
|
|
RNGestureHandlerRegistry *_registry;
|
|
RCTUIManager *_uiManager;
|
|
NSHashTable<UIView *> *_rootViews;
|
|
RCTEventDispatcher *_eventDispatcher;
|
|
}
|
|
|
|
- (instancetype)initWithUIManager:(RCTUIManager *)uiManager
|
|
eventDispatcher:(RCTEventDispatcher *)eventDispatcher
|
|
{
|
|
if ((self = [super init])) {
|
|
_uiManager = uiManager;
|
|
_eventDispatcher = eventDispatcher;
|
|
_registry = [RNGestureHandlerRegistry new];
|
|
_rootViews = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)createGestureHandler:(NSString *)handlerName
|
|
tag:(NSNumber *)handlerTag
|
|
config:(NSDictionary *)config
|
|
{
|
|
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],
|
|
};
|
|
});
|
|
|
|
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
|
|
{
|
|
UIView *view = [_uiManager viewForReactTag:viewTag];
|
|
|
|
[_registry attachHandlerWithTag:handlerTag toView:view];
|
|
|
|
// register root view if not already there
|
|
[self registerRootViewIfNeeded:view];
|
|
}
|
|
|
|
- (void)updateGestureHandler:(NSNumber *)handlerTag config:(NSDictionary *)config
|
|
{
|
|
RNGestureHandler *handler = [_registry handlerWithTag:handlerTag];
|
|
[handler configure:config];
|
|
}
|
|
|
|
- (void)dropGestureHandler:(NSNumber *)handlerTag
|
|
{
|
|
[_registry dropHandlerWithTag:handlerTag];
|
|
}
|
|
|
|
- (void)handleSetJSResponder:(NSNumber *)viewTag blockNativeResponder:(NSNumber *)blockNativeResponder
|
|
{
|
|
if ([blockNativeResponder boolValue]) {
|
|
for (RCTRootView *rootView in _rootViews) {
|
|
for (UIGestureRecognizer *recognizer in rootView.gestureRecognizers) {
|
|
if ([recognizer isKindOfClass:[RNRootViewGestureRecognizer class]]) {
|
|
[(RNRootViewGestureRecognizer *)recognizer blockOtherRecognizers];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)handleClearJSResponder
|
|
{
|
|
// ignore...
|
|
}
|
|
|
|
#pragma mark Root Views Management
|
|
|
|
- (void)registerRootViewIfNeeded:(UIView*)childView
|
|
{
|
|
UIView *parent = childView;
|
|
while (parent != nil && ![parent isKindOfClass:[RCTRootView class]]) parent = parent.superview;
|
|
|
|
RCTRootView *rootView = (RCTRootView *)parent;
|
|
UIView *rootContentView = rootView.contentView;
|
|
if (rootContentView != nil && ![_rootViews containsObject:rootContentView]) {
|
|
RCTLifecycleLog(@"[GESTURE HANDLER] Initialize gesture handler for root view %@", rootContentView);
|
|
[_rootViews addObject:rootContentView];
|
|
RNRootViewGestureRecognizer *recognizer = [RNRootViewGestureRecognizer new];
|
|
recognizer.delegate = self;
|
|
rootContentView.userInteractionEnabled = YES;
|
|
[rootContentView addGestureRecognizer:recognizer];
|
|
}
|
|
}
|
|
|
|
- (void)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
|
didActivateInRootView:(UIView *)rootContentView
|
|
{
|
|
// 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 ([gestureRecognizer.view isKindOfClass:[UIScrollView class]]) return;
|
|
|
|
UIView *parent = rootContentView.superview;
|
|
if ([parent isKindOfClass:[RCTRootView class]]) {
|
|
[((RCTRootContentView*)rootContentView).touchHandler cancel];
|
|
}
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
if ([_rootViews count] > 0) {
|
|
RCTLifecycleLog(@"[GESTURE HANDLER] Tearing down gesture handler registered for views %@", _rootViews);
|
|
}
|
|
}
|
|
|
|
#pragma mark Events
|
|
|
|
- (void)sendTouchEvent:(RNGestureHandlerEvent *)event
|
|
{
|
|
[_eventDispatcher sendEvent:event];
|
|
}
|
|
|
|
- (void)sendStateChangeEvent:(RNGestureHandlerStateChange *)event
|
|
{
|
|
[_eventDispatcher sendEvent:event];
|
|
}
|
|
|
|
@end
|