1*f67462bcSTomasz Sapeta// Copyright 2015-present 650 Industries. All rights reserved. 2*f67462bcSTomasz Sapeta 3*f67462bcSTomasz Sapeta#import <React/RCTUtils.h> 4*f67462bcSTomasz Sapeta 5*f67462bcSTomasz Sapeta#import "EXDevMenuGestureInterceptor.h" 6*f67462bcSTomasz Sapeta#import "EXDevMenuGestureRecognizer.h" 7*f67462bcSTomasz Sapeta 8*f67462bcSTomasz Sapeta@import UIKit; 9*f67462bcSTomasz Sapeta 10*f67462bcSTomasz Sapetastatic BOOL isInstalled = NO; 11*f67462bcSTomasz Sapetastatic EXDevMenuGestureRecognizer *gestureRecognizerInstance; 12*f67462bcSTomasz Sapeta 13*f67462bcSTomasz Sapeta@implementation UIWindow (EXDevMenuGestureInterceptor) 14*f67462bcSTomasz Sapeta 15*f67462bcSTomasz Sapeta- (NSArray<__kindof UIGestureRecognizer *> *)EX_gestureRecognizers 16*f67462bcSTomasz Sapeta{ 17*f67462bcSTomasz Sapeta // Just for thread safety, someone may uninstall the interceptor in the meantime and we would fall into recursive loop. 18*f67462bcSTomasz Sapeta if (!isInstalled) { 19*f67462bcSTomasz Sapeta return [self gestureRecognizers]; 20*f67462bcSTomasz Sapeta } 21*f67462bcSTomasz Sapeta 22*f67462bcSTomasz Sapeta // Check for the case where singleton instance of gesture recognizer is not created yet or is attached to different window. 23*f67462bcSTomasz Sapeta if (!gestureRecognizerInstance || gestureRecognizerInstance.view != self) { 24*f67462bcSTomasz Sapeta // Create gesture recognizer if not created yet. 25*f67462bcSTomasz Sapeta static dispatch_once_t onceToken; 26*f67462bcSTomasz Sapeta dispatch_once(&onceToken, ^{ 27*f67462bcSTomasz Sapeta gestureRecognizerInstance = [EXDevMenuGestureRecognizer new]; 28*f67462bcSTomasz Sapeta }); 29*f67462bcSTomasz Sapeta 30*f67462bcSTomasz Sapeta // Remove it from the window it's attached to. 31*f67462bcSTomasz Sapeta [gestureRecognizerInstance.view removeGestureRecognizer:gestureRecognizerInstance]; 32*f67462bcSTomasz Sapeta 33*f67462bcSTomasz Sapeta // Attach to this window. 34*f67462bcSTomasz Sapeta [self addGestureRecognizer:gestureRecognizerInstance]; 35*f67462bcSTomasz Sapeta } 36*f67462bcSTomasz Sapeta 37*f67462bcSTomasz Sapeta // `EX_gestureRecognizers` implementation has been swapped with `gestureRecognizers` - it might be confusing that we call it recursively, but we don't. 38*f67462bcSTomasz Sapeta return [self EX_gestureRecognizers]; 39*f67462bcSTomasz Sapeta} 40*f67462bcSTomasz Sapeta 41*f67462bcSTomasz Sapeta@end 42*f67462bcSTomasz Sapeta 43*f67462bcSTomasz Sapeta@implementation EXDevMenuGestureInterceptor 44*f67462bcSTomasz Sapeta 45*f67462bcSTomasz Sapeta+ (void)install 46*f67462bcSTomasz Sapeta{ 47*f67462bcSTomasz Sapeta if (!isInstalled) { 48*f67462bcSTomasz Sapeta // Capture touch gesture from any window by swapping default implementation from UIWindow. 49*f67462bcSTomasz Sapeta RCTSwapInstanceMethods([UIWindow class], @selector(gestureRecognizers), @selector(EX_gestureRecognizers)); 50*f67462bcSTomasz Sapeta isInstalled = YES; 51*f67462bcSTomasz Sapeta 52*f67462bcSTomasz Sapeta // Make sure gesture recognizer is enabled. 53*f67462bcSTomasz Sapeta [gestureRecognizerInstance setEnabled:YES]; 54*f67462bcSTomasz Sapeta } 55*f67462bcSTomasz Sapeta} 56*f67462bcSTomasz Sapeta 57*f67462bcSTomasz Sapeta+ (void)uninstall 58*f67462bcSTomasz Sapeta{ 59*f67462bcSTomasz Sapeta if (isInstalled) { 60*f67462bcSTomasz Sapeta // Bring back the original method. 61*f67462bcSTomasz Sapeta RCTSwapInstanceMethods([UIWindow class], @selector(gestureRecognizers), @selector(EX_gestureRecognizers)); 62*f67462bcSTomasz Sapeta isInstalled = NO; 63*f67462bcSTomasz Sapeta 64*f67462bcSTomasz Sapeta // Cancel recognized gestures and disable it entirely. 65*f67462bcSTomasz Sapeta [gestureRecognizerInstance setEnabled:NO]; 66*f67462bcSTomasz Sapeta } 67*f67462bcSTomasz Sapeta} 68*f67462bcSTomasz Sapeta 69*f67462bcSTomasz Sapeta+ (BOOL)isInstalled 70*f67462bcSTomasz Sapeta{ 71*f67462bcSTomasz Sapeta return isInstalled; 72*f67462bcSTomasz Sapeta} 73*f67462bcSTomasz Sapeta 74*f67462bcSTomasz Sapeta@end 75