RCTSurfaceTouchHandler.mm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. #import "RCTSurfaceTouchHandler.h"
  8. #import <React/RCTUtils.h>
  9. #import <React/RCTViewComponentView.h>
  10. #import <UIKit/UIGestureRecognizerSubclass.h>
  11. #import "RCTConversions.h"
  12. #import "RCTTouchableComponentViewProtocol.h"
  13. using namespace facebook::react;
  14. template <size_t size>
  15. class IdentifierPool {
  16. public:
  17. void enqueue(int index)
  18. {
  19. usage[index] = false;
  20. }
  21. int dequeue()
  22. {
  23. while (true) {
  24. if (!usage[lastIndex]) {
  25. usage[lastIndex] = true;
  26. return lastIndex;
  27. }
  28. lastIndex = (lastIndex + 1) % size;
  29. }
  30. }
  31. void reset()
  32. {
  33. for (int i = 0; i < size; i++) {
  34. usage[i] = false;
  35. }
  36. }
  37. private:
  38. bool usage[size];
  39. int lastIndex;
  40. };
  41. typedef NS_ENUM(NSInteger, RCTTouchEventType) {
  42. RCTTouchEventTypeTouchStart,
  43. RCTTouchEventTypeTouchMove,
  44. RCTTouchEventTypeTouchEnd,
  45. RCTTouchEventTypeTouchCancel,
  46. };
  47. struct ActiveTouch {
  48. Touch touch;
  49. SharedTouchEventEmitter eventEmitter;
  50. /*
  51. * A component view on which the touch was begun.
  52. */
  53. __strong UIView<RCTComponentViewProtocol> *componentView = nil;
  54. struct Hasher {
  55. size_t operator()(const ActiveTouch &activeTouch) const
  56. {
  57. return std::hash<decltype(activeTouch.touch.identifier)>()(activeTouch.touch.identifier);
  58. }
  59. };
  60. struct Comparator {
  61. bool operator()(const ActiveTouch &lhs, const ActiveTouch &rhs) const
  62. {
  63. return lhs.touch.identifier == rhs.touch.identifier;
  64. }
  65. };
  66. };
  67. static void UpdateActiveTouchWithUITouch(ActiveTouch &activeTouch, UITouch *uiTouch, UIView *rootComponentView)
  68. {
  69. CGPoint offsetPoint = [uiTouch locationInView:activeTouch.componentView];
  70. CGPoint screenPoint = [uiTouch locationInView:uiTouch.window];
  71. CGPoint pagePoint = [uiTouch locationInView:rootComponentView];
  72. activeTouch.touch.offsetPoint = RCTPointFromCGPoint(offsetPoint);
  73. activeTouch.touch.screenPoint = RCTPointFromCGPoint(screenPoint);
  74. activeTouch.touch.pagePoint = RCTPointFromCGPoint(pagePoint);
  75. activeTouch.touch.timestamp = uiTouch.timestamp;
  76. if (RCTForceTouchAvailable()) {
  77. activeTouch.touch.force = uiTouch.force / uiTouch.maximumPossibleForce;
  78. }
  79. }
  80. static ActiveTouch CreateTouchWithUITouch(UITouch *uiTouch, UIView *rootComponentView)
  81. {
  82. UIView *componentView = uiTouch.view;
  83. ActiveTouch activeTouch = {};
  84. if ([componentView respondsToSelector:@selector(touchEventEmitterAtPoint:)]) {
  85. activeTouch.eventEmitter = [(id<RCTTouchableComponentViewProtocol>)componentView
  86. touchEventEmitterAtPoint:[uiTouch locationInView:componentView]];
  87. activeTouch.touch.target = (Tag)componentView.tag;
  88. }
  89. activeTouch.componentView = componentView;
  90. UpdateActiveTouchWithUITouch(activeTouch, uiTouch, rootComponentView);
  91. return activeTouch;
  92. }
  93. static BOOL AllTouchesAreCancelledOrEnded(NSSet<UITouch *> *touches)
  94. {
  95. for (UITouch *touch in touches) {
  96. if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved || touch.phase == UITouchPhaseStationary) {
  97. return NO;
  98. }
  99. }
  100. return YES;
  101. }
  102. static BOOL AnyTouchesChanged(NSSet<UITouch *> *touches)
  103. {
  104. for (UITouch *touch in touches) {
  105. if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved) {
  106. return YES;
  107. }
  108. }
  109. return NO;
  110. }
  111. /**
  112. * Surprisingly, `__unsafe_unretained id` pointers are not regular pointers
  113. * and `std::hash<>` cannot hash them.
  114. * This is quite trivial but decent implementation of hasher function
  115. * inspired by this research: https://stackoverflow.com/a/21062520/496389.
  116. */
  117. template <typename PointerT>
  118. struct PointerHasher {
  119. constexpr std::size_t operator()(const PointerT &value) const
  120. {
  121. return reinterpret_cast<size_t>(value);
  122. }
  123. };
  124. @interface RCTSurfaceTouchHandler () <UIGestureRecognizerDelegate>
  125. @end
  126. @implementation RCTSurfaceTouchHandler {
  127. std::unordered_map<__unsafe_unretained UITouch *, ActiveTouch, PointerHasher<__unsafe_unretained UITouch *>>
  128. _activeTouches;
  129. UIView *_rootComponentView;
  130. IdentifierPool<11> _identifierPool;
  131. }
  132. - (instancetype)init
  133. {
  134. if (self = [super initWithTarget:nil action:nil]) {
  135. // `cancelsTouchesInView` and `delaysTouches*` are needed in order
  136. // to be used as a top level event delegated recognizer.
  137. // Otherwise, lower-level components not built using React Native,
  138. // will fail to recognize gestures.
  139. self.cancelsTouchesInView = NO;
  140. self.delaysTouchesBegan = NO; // This is default value.
  141. self.delaysTouchesEnded = NO;
  142. self.delegate = self;
  143. }
  144. return self;
  145. }
  146. RCT_NOT_IMPLEMENTED(-(instancetype)initWithTarget : (id)target action : (SEL)action)
  147. - (void)attachToView:(UIView *)view
  148. {
  149. RCTAssert(self.view == nil, @"RCTTouchHandler already has attached view.");
  150. [view addGestureRecognizer:self];
  151. _rootComponentView = view;
  152. }
  153. - (void)detachFromView:(UIView *)view
  154. {
  155. RCTAssertParam(view);
  156. RCTAssert(self.view == view, @"RCTTouchHandler attached to another view.");
  157. [view removeGestureRecognizer:self];
  158. _rootComponentView = nil;
  159. }
  160. - (void)_registerTouches:(NSSet<UITouch *> *)touches
  161. {
  162. for (UITouch *touch in touches) {
  163. auto activeTouch = CreateTouchWithUITouch(touch, _rootComponentView);
  164. activeTouch.touch.identifier = _identifierPool.dequeue();
  165. _activeTouches.emplace(touch, activeTouch);
  166. }
  167. }
  168. - (void)_updateTouches:(NSSet<UITouch *> *)touches
  169. {
  170. for (UITouch *touch in touches) {
  171. auto iterator = _activeTouches.find(touch);
  172. assert(iterator != _activeTouches.end() && "Inconsistency between local and UIKit touch registries");
  173. if (iterator == _activeTouches.end()) {
  174. continue;
  175. }
  176. UpdateActiveTouchWithUITouch(iterator->second, touch, _rootComponentView);
  177. }
  178. }
  179. - (void)_unregisterTouches:(NSSet<UITouch *> *)touches
  180. {
  181. for (UITouch *touch in touches) {
  182. auto iterator = _activeTouches.find(touch);
  183. assert(iterator != _activeTouches.end() && "Inconsistency between local and UIKit touch registries");
  184. if (iterator == _activeTouches.end()) {
  185. continue;
  186. }
  187. auto &activeTouch = iterator->second;
  188. _identifierPool.enqueue(activeTouch.touch.identifier);
  189. _activeTouches.erase(touch);
  190. }
  191. }
  192. - (std::vector<ActiveTouch>)_activeTouchesFromTouches:(NSSet<UITouch *> *)touches
  193. {
  194. std::vector<ActiveTouch> activeTouches;
  195. activeTouches.reserve(touches.count);
  196. for (UITouch *touch in touches) {
  197. auto iterator = _activeTouches.find(touch);
  198. assert(iterator != _activeTouches.end() && "Inconsistency between local and UIKit touch registries");
  199. if (iterator == _activeTouches.end()) {
  200. continue;
  201. }
  202. activeTouches.push_back(iterator->second);
  203. }
  204. return activeTouches;
  205. }
  206. - (void)_dispatchActiveTouches:(std::vector<ActiveTouch>)activeTouches eventType:(RCTTouchEventType)eventType
  207. {
  208. TouchEvent event = {};
  209. std::unordered_set<ActiveTouch, ActiveTouch::Hasher, ActiveTouch::Comparator> changedActiveTouches = {};
  210. std::unordered_set<SharedTouchEventEmitter> uniqueEventEmitters = {};
  211. BOOL isEndishEventType = eventType == RCTTouchEventTypeTouchEnd || eventType == RCTTouchEventTypeTouchCancel;
  212. for (const auto &activeTouch : activeTouches) {
  213. if (!activeTouch.eventEmitter) {
  214. continue;
  215. }
  216. changedActiveTouches.insert(activeTouch);
  217. event.changedTouches.insert(activeTouch.touch);
  218. uniqueEventEmitters.insert(activeTouch.eventEmitter);
  219. }
  220. for (const auto &pair : _activeTouches) {
  221. if (!pair.second.eventEmitter) {
  222. continue;
  223. }
  224. if (isEndishEventType && event.changedTouches.find(pair.second.touch) != event.changedTouches.end()) {
  225. continue;
  226. }
  227. event.touches.insert(pair.second.touch);
  228. }
  229. for (const auto &eventEmitter : uniqueEventEmitters) {
  230. event.targetTouches.clear();
  231. for (const auto &pair : _activeTouches) {
  232. if (pair.second.eventEmitter == eventEmitter) {
  233. event.targetTouches.insert(pair.second.touch);
  234. }
  235. }
  236. switch (eventType) {
  237. case RCTTouchEventTypeTouchStart:
  238. eventEmitter->onTouchStart(event);
  239. break;
  240. case RCTTouchEventTypeTouchMove:
  241. eventEmitter->onTouchMove(event);
  242. break;
  243. case RCTTouchEventTypeTouchEnd:
  244. eventEmitter->onTouchEnd(event);
  245. break;
  246. case RCTTouchEventTypeTouchCancel:
  247. eventEmitter->onTouchCancel(event);
  248. break;
  249. }
  250. }
  251. }
  252. #pragma mark - `UIResponder`-ish touch-delivery methods
  253. - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  254. {
  255. [super touchesBegan:touches withEvent:event];
  256. [self _registerTouches:touches];
  257. [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchStart];
  258. if (self.state == UIGestureRecognizerStatePossible) {
  259. self.state = UIGestureRecognizerStateBegan;
  260. } else if (self.state == UIGestureRecognizerStateBegan) {
  261. self.state = UIGestureRecognizerStateChanged;
  262. }
  263. }
  264. - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  265. {
  266. [super touchesMoved:touches withEvent:event];
  267. [self _updateTouches:touches];
  268. [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchMove];
  269. self.state = UIGestureRecognizerStateChanged;
  270. }
  271. - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  272. {
  273. [super touchesEnded:touches withEvent:event];
  274. [self _updateTouches:touches];
  275. [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchEnd];
  276. [self _unregisterTouches:touches];
  277. if (AllTouchesAreCancelledOrEnded(event.allTouches)) {
  278. self.state = UIGestureRecognizerStateEnded;
  279. } else if (AnyTouchesChanged(event.allTouches)) {
  280. self.state = UIGestureRecognizerStateChanged;
  281. }
  282. }
  283. - (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
  284. {
  285. [super touchesCancelled:touches withEvent:event];
  286. [self _updateTouches:touches];
  287. [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] eventType:RCTTouchEventTypeTouchCancel];
  288. [self _unregisterTouches:touches];
  289. if (AllTouchesAreCancelledOrEnded(event.allTouches)) {
  290. self.state = UIGestureRecognizerStateCancelled;
  291. } else if (AnyTouchesChanged(event.allTouches)) {
  292. self.state = UIGestureRecognizerStateChanged;
  293. }
  294. }
  295. - (void)reset
  296. {
  297. [super reset];
  298. if (_activeTouches.size() != 0) {
  299. std::vector<ActiveTouch> activeTouches;
  300. activeTouches.reserve(_activeTouches.size());
  301. for (auto const &pair : _activeTouches) {
  302. activeTouches.push_back(pair.second);
  303. }
  304. [self _dispatchActiveTouches:activeTouches eventType:RCTTouchEventTypeTouchCancel];
  305. // Force-unregistering all the touches.
  306. _activeTouches.clear();
  307. _identifierPool.reset();
  308. }
  309. }
  310. - (BOOL)canPreventGestureRecognizer:(__unused UIGestureRecognizer *)preventedGestureRecognizer
  311. {
  312. return NO;
  313. }
  314. - (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
  315. {
  316. // We fail in favour of other external gesture recognizers.
  317. // iOS will ask `delegate`'s opinion about this gesture recognizer little bit later.
  318. return ![preventingGestureRecognizer.view isDescendantOfView:self.view];
  319. }
  320. #pragma mark - UIGestureRecognizerDelegate
  321. - (BOOL)gestureRecognizer:(__unused UIGestureRecognizer *)gestureRecognizer
  322. shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
  323. {
  324. // Same condition for `failure of` as for `be prevented by`.
  325. return [self canBePreventedByGestureRecognizer:otherGestureRecognizer];
  326. }
  327. @end