From 560652cfe86d50243d145f860b0b5dcc4a64bf58 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Fri, 16 Nov 2018 18:18:57 -0800 Subject: [PATCH] Fabric: Proper implementation `-[RCTSurfaceTouchHandler reset]` Summary: Apparently, if a gesture recognizer got 'reset', OS does not call `touchesCancelled:` method, so we have to do it manually. To implement this we have to split `_dispatchTouches:eventType:` into two methods: the first converts Objective-C touches to C++ touches, the second consumes that. We have to do this because during reset we don't have a collection of UIKit touches. Reviewed By: mdvacca Differential Revision: D13072807 fbshipit-source-id: 677e2febf9f96dcdfaadfadf5b9ab3893f93e796 --- React/Fabric/RCTSurfaceTouchHandler.mm | 51 ++++++++++++++++++++------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/React/Fabric/RCTSurfaceTouchHandler.mm b/React/Fabric/RCTSurfaceTouchHandler.mm index d25653a74..14d1a807f 100644 --- a/React/Fabric/RCTSurfaceTouchHandler.mm +++ b/React/Fabric/RCTSurfaceTouchHandler.mm @@ -209,16 +209,26 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action } } -- (void)_dispatchTouches:(NSSet *)touches eventType:(RCTTouchEventType)eventType +- (std::vector)_activeTouchesFromTouches:(NSSet *)touches +{ + std::vector activeTouches; + activeTouches.reserve(touches.count); + + for (UITouch *touch in touches) { + activeTouches.push_back(_activeTouches.at(touch)); + } + + return activeTouches; +} + +- (void)_dispatchActiveTouches:(std::vector)activeTouches eventType:(RCTTouchEventType)eventType { TouchEvent event = {}; std::unordered_set changedActiveTouches = {}; std::unordered_set uniqueEventEmitter = {}; BOOL isEndishEventType = eventType == RCTTouchEventTypeTouchEnd || eventType == RCTTouchEventTypeTouchCancel; - for (UITouch *touch in touches) { - const auto &activeTouch = _activeTouches[touch]; - + for (const auto &activeTouch : activeTouches) { if (!activeTouch.eventEmitter) { continue; } @@ -276,7 +286,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action [super touchesBegan:touches withEvent:event]; [self _registerTouches:touches]; - [self _dispatchTouches:touches eventType:RCTTouchEventTypeTouchStart]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] + eventType:RCTTouchEventTypeTouchStart]; if (self.state == UIGestureRecognizerStatePossible) { self.state = UIGestureRecognizerStateBegan; @@ -290,7 +301,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action [super touchesMoved:touches withEvent:event]; [self _updateTouches:touches]; - [self _dispatchTouches:touches eventType:RCTTouchEventTypeTouchMove]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] + eventType:RCTTouchEventTypeTouchMove]; self.state = UIGestureRecognizerStateChanged; } @@ -300,7 +312,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action [super touchesEnded:touches withEvent:event]; [self _updateTouches:touches]; - [self _dispatchTouches:touches eventType:RCTTouchEventTypeTouchEnd]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] + eventType:RCTTouchEventTypeTouchEnd]; [self _unregisterTouches:touches]; if (AllTouchesAreCancelledOrEnded(event.allTouches)) { @@ -315,7 +328,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action [super touchesCancelled:touches withEvent:event]; [self _updateTouches:touches]; - [self _dispatchTouches:touches eventType:RCTTouchEventTypeTouchCancel]; + [self _dispatchActiveTouches:[self _activeTouchesFromTouches:touches] + eventType:RCTTouchEventTypeTouchCancel]; [self _unregisterTouches:touches]; if (AllTouchesAreCancelledOrEnded(event.allTouches)) { @@ -327,10 +341,23 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithTarget:(id)target action:(SEL)action - (void)reset { - // Technically, `_activeTouches` must be already empty at this point, - // but just to be sure, we clear it explicitly. - _activeTouches.clear(); - _identifierPool.reset(); + [super reset]; + + if (_activeTouches.size() != 0) { + std::vector activeTouches; + activeTouches.reserve(_activeTouches.size()); + + for (auto const &pair : _activeTouches) { + activeTouches.push_back(pair.second); + } + + [self _dispatchActiveTouches:activeTouches + eventType:RCTTouchEventTypeTouchCancel]; + + // Force-unregistering all the touches. + _activeTouches.clear(); + _identifierPool.reset(); + } } - (BOOL)canPreventGestureRecognizer:(__unused UIGestureRecognizer *)preventedGestureRecognizer