ComponentKit/Core/Action/CKComponentGestureActions.mm (110 lines of code) (raw):

/* * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * */ #import "CKComponentGestureActions.h" #import "CKComponentGestureActionHelper.h" #import <vector> #import <objc/runtime.h> #import "CKFatal.h" #import "CKComponent+UIView.h" #import "CKComponentGestureActionsInternal.h" #import <ComponentKit/CKInternalHelpers.h> #import <ComponentKit/CKComponentInternal.h> CKComponentViewAttributeValue CKComponentTapGestureAttribute(CKAction<UIGestureRecognizer *> action) { return CKComponentGestureAttribute([UITapGestureRecognizer class], nullptr, action); } CKComponentViewAttributeValue CKComponentPanGestureAttribute(CKAction<UIGestureRecognizer *> action) { return CKComponentGestureAttribute([UIPanGestureRecognizer class], nullptr, action); } CKComponentViewAttributeValue CKComponentLongPressGestureAttribute(CKAction<UIGestureRecognizer *> action) { return CKComponentGestureAttribute([UILongPressGestureRecognizer class], nullptr, action); } CKComponentViewAttributeValue CKComponentGestureAttributeInternal(Class gestureRecognizerClass, CKComponentGestureRecognizerSetupFunction setupFunction, CKAction<UIGestureRecognizer *> action, const std::string& identifierSuffix, void (^applicatorBlock)(UIView *, UIGestureRecognizer *), void (^unapplicatorBlock)(UIGestureRecognizer *)) { if (!action || gestureRecognizerClass == Nil) { return { { std::string(class_getName(gestureRecognizerClass)) + "-" + CKStringFromPointer((const void *)setupFunction) + "-no-op", ^(UIView *view, id value) {}, ^(UIView *view, id value) {} }, @YES // Bogus value, we don't use it. }; } auto reusePool = CKCreateOrGetReusePool(gestureRecognizerClass, setupFunction); CKAction<UIGestureRecognizer *> blockAction = action; return { { std::string(class_getName(gestureRecognizerClass)) + "-" + CKStringFromPointer((const void *)setupFunction) + "-" + action.identifier() + identifierSuffix, ^(UIView *view, id value){ RCCAssertNil(CKRecognizerForAction(view, blockAction), @"Registered two gesture recognizers with the same action %@", NSStringFromSelector(blockAction.selector())); UIGestureRecognizer *gestureRecognizer = reusePool->get(); CKSetComponentActionForGestureRecognizer(gestureRecognizer, blockAction); if (applicatorBlock != nil) { applicatorBlock(view, gestureRecognizer); } @try { [view addGestureRecognizer:gestureRecognizer]; } @catch (NSException *ex) { CKComponent *mountedComponent = CKMountedComponentForView(view); NSString *fatalMsg = @"View does not have a mountedComponent"; if (mountedComponent) { fatalMsg = [mountedComponent backtraceStackDescription]; } CKCFatalWithCategory(mountedComponent.className, @"%@ while mounting \n%@", ex, fatalMsg); } }, ^(UIView *view, id value){ UIGestureRecognizer *recognizer = CKRecognizerForAction(view, blockAction); if (recognizer == nil) { return; } [view removeGestureRecognizer:recognizer]; CKUnsetComponentActionForGestureRecognizer(recognizer); if (unapplicatorBlock != nil) { unapplicatorBlock(recognizer); } reusePool->recycle(recognizer); } }, @YES // Bogus value, we don't use it. }; } CKComponentViewAttributeValue CKComponentGestureAttribute(Class gestureRecognizerClass, CKComponentGestureRecognizerSetupFunction setupFunction, CKAction<UIGestureRecognizer *> action, CKComponentForwardedSelectors delegateSelectors) { return CKComponentGestureAttributeInternal( gestureRecognizerClass, setupFunction, action, CKIdentifierFromDelegateForwarderSelectors(delegateSelectors), ^(UIView *view, UIGestureRecognizer *recognizer) { // Setup delegate proxying if applicable if (delegateSelectors.size() > 0) { RCCAssertNil(recognizer.delegate, @"Doesn't make sense to set the gesture delegate and provide selectors to proxy"); CKComponentDelegateForwarder *proxy = [CKComponentDelegateForwarder newWithSelectors:delegateSelectors]; proxy.view = view; recognizer.delegate = (id<UIGestureRecognizerDelegate>)proxy; // This will retain it CKSetDelegateProxyForObject(recognizer, proxy); } }, ^(UIGestureRecognizer *recognizer){ // Tear down delegate proxying if applicable if (delegateSelectors.size() > 0) { CKComponentDelegateForwarder *proxy = CKDelegateProxyForObject(recognizer); proxy.view = nil; recognizer.delegate = nil; CKSetDelegateProxyForObject(recognizer, nil); } } ); }