diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.h b/packages/react-native-gesture-handler/apple/RNGestureHandler.h index edf1ae9ed3..c479be3847 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.h @@ -85,6 +85,7 @@ @property (nonatomic) BOOL dispatchesReanimatedEvents; @property (nonatomic, weak, nullable) RNGHUIView *hostDetectorView; @property (nonatomic, nullable, assign) NSNumber *virtualViewTag; +@property (nonatomic, copy, nullable) NSNumber *viewTag; - (BOOL)isViewParagraphComponent:(nullable RNGHUIView *)view; - (nonnull RNGHUIView *)chooseViewForInteraction:(nonnull UIGestureRecognizer *)recognizer; diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm index 72be857c69..30283b337e 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandler.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandler.mm @@ -262,6 +262,8 @@ - (void)bindToView:(RNGHUIView *)view [recognizerView addGestureRecognizer:self.recognizer]; [self bindManualActivationToView:recognizerView]; + + self.viewTag = view.reactTag; } - (void)unbindFromView @@ -271,6 +273,7 @@ - (void)unbindFromView self.hostDetectorView = nil; self.virtualViewTag = nil; + self.viewTag = nil; [self unbindManualActivation]; } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h index 7e83b286eb..970de2d143 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.h @@ -42,4 +42,6 @@ - (nullable RNGestureHandler *)handlerWithTag:(nonnull NSNumber *)handlerTag; - (nullable RNGHUIView *)viewForReactTag:(nonnull NSNumber *)reactTag; + +- (void)reattachHandlersIfNeeded; @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm index 319ef9efbd..e19c63ca60 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerManager.mm @@ -171,6 +171,21 @@ - (void)attachGestureHandler:(nonnull NSNumber *)handlerTag } [_attachRetryCounter removeObjectForKey:viewTag]; + [self maybeBindHandler:handlerTag toViewWithTag:viewTag withActionType:actionType withHostDetector:hostDetector]; +} + +// Resolves a view tag to its native UIView (including contentView unwrapping), +// sets reactTag, and binds the gesture handler to it. No-op if the handler is +// already attached to the correct view. +- (void)maybeBindHandler:(nonnull NSNumber *)handlerTag + toViewWithTag:(nonnull NSNumber *)viewTag + withActionType:(RNGestureHandlerActionType)actionType + withHostDetector:(nullable RNGHUIView *)hostDetector +{ + RNGHUIView *view = [_viewRegistry viewForReactTag:viewTag]; + if (view == nil || view.superview == nil) { + return; + } // I think it should be moved to RNNativeViewHandler, but that would require // additional logic for setting contentView.reactTag, this works for now @@ -181,12 +196,17 @@ - (void)attachGestureHandler:(nonnull NSNumber *)handlerTag } } + RNGestureHandler *handler = [_registry handlerWithTag:handlerTag]; + + // Already attached to the correct native view, nothing to do. + if (handler != nil && handler.recognizer.view == view && handler.actionType == actionType) { + return; + } + view.reactTag = viewTag; // necessary for RNReanimated eventHash (e.g. "42onGestureHandlerEvent"), also will be // returned as event.target [_registry attachHandlerWithTag:handlerTag toView:view withActionType:actionType withHostDetector:hostDetector]; - - // register view if not already there [self registerViewWithGestureRecognizerAttachedIfNeeded:view]; } @@ -224,6 +244,24 @@ - (id)handlerWithTag:(NSNumber *)handlerTag return [_registry handlerWithTag:handlerTag]; } +- (void)reattachHandlersIfNeeded +{ + // Re-bind handlers to their current native views. On Fabric, when a parent view has + // display:none and siblings change, the native UIView backing a component may be recycled + // and replaced. maybeBindHandler is a no-op if the view is nil or unchanged. This is only + // needed for handlers using the old api. + for (RNGestureHandler *handler in _registry.handlers.objectEnumerator) { + if (handler.viewTag == nil || [handler usesNativeOrVirtualDetector]) { + continue; + } + + [self maybeBindHandler:handler.tag + toViewWithTag:handler.viewTag + withActionType:handler.actionType + withHostDetector:nil]; + } +} + #pragma mark Root Views Management - (void)registerViewWithGestureRecognizerAttachedIfNeeded:(RNGHUIView *)childView diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm index 0f224571f0..e3d6ac0450 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm @@ -171,18 +171,21 @@ - (void)flushOperations { // On the new arch we rely on `flushOperations` for scheduling the operations on the UI thread. // On the old arch we rely on `uiManagerWillPerformMounting` - if (_operations.count == 0) { - return; - } - RNGestureHandlerManager *manager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId]; - NSArray *operations = _operations; - _operations = [NSMutableArray new]; + + if (_operations.count > 0) { + NSArray *operations = _operations; + _operations = [NSMutableArray new]; + + [self.viewRegistry_DEPRECATED addUIBlock:^(RCTViewRegistry *viewRegistry) { + for (GestureHandlerOperation operation in operations) { + operation(manager); + } + }]; + } [self.viewRegistry_DEPRECATED addUIBlock:^(RCTViewRegistry *viewRegistry) { - for (GestureHandlerOperation operation in operations) { - operation(manager); - } + [manager reattachHandlersIfNeeded]; }]; } diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h index da2fb97f91..0122de2db6 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.h @@ -21,4 +21,6 @@ - (void)dropHandlerWithTag:(nonnull NSNumber *)handlerTag; - (void)dropAllHandlers; +@property (nonatomic, readonly, nonnull) NSDictionary *handlers; + @end diff --git a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m index 8192f6d0c5..e2245902c3 100644 --- a/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m +++ b/packages/react-native-gesture-handler/apple/RNGestureHandlerRegistry.m @@ -22,6 +22,11 @@ - (instancetype)init return self; } +- (NSDictionary *)handlers +{ + return _handlers; +} + - (RNGestureHandler *)handlerWithTag:(NSNumber *)handlerTag { return _handlers[handlerTag];