Skip to content

Commit cd27bfd

Browse files
authored
Fix native gesture reattach (#3672)
## Description In `NativeDetector` native gesture failed to reattach when child changed. ## Test plan Add a logging message in attach/detach in `android/.../react/RNGestureHandlerRegistry.kt` and `apple/RNGestureHandlerRegistry.m` ` ```ts import * as React from 'react'; import { StyleSheet, Text, View, ScrollView, Button } from 'react-native'; import { GestureHandlerRootView, NativeDetector, useGesture, } from 'react-native-gesture-handler'; export default function App() { const items = Array.from({ length: 5 }, (_, index) => `Item ${index + 1}`); const [enabled, setEnabled] = React.useState(true); const gesture = useGesture('NativeViewGestureHandler', { onBegin: (e) => { console.log('onBegin'); }, onStart: (e) => { console.log('onStart'); } }); const SV1 = () => ( <ScrollView style={styles.scrollView1}> {items.map((item, index) => ( <View key={index} style={styles.item}> <Text style={styles.text}>{item}</Text> </View> ))} </ScrollView> ); const SV2 = () => ( <ScrollView style={styles.scrollView2}> {items.map((item, index) => ( <View key={index} style={styles.item}> <Text style={styles.text}>{item}</Text> </View> ))} </ScrollView> ); return ( <GestureHandlerRootView style={{ flex: 1, backgroundColor: 'white', paddingTop: 100 }}> <Button title="Swap the child" onPress={() => { setEnabled(!enabled); }} /> <NativeDetector gesture={gesture}> {enabled ? <SV1 /> : <SV2 />} </NativeDetector > </GestureHandlerRootView > ); } const styles = StyleSheet.create({ scrollView1: { backgroundColor: 'pink', marginHorizontal: 20, }, scrollView2: { backgroundColor: 'lightblue', marginHorizontal: 20, }, item: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', padding: 20, margin: 2, backgroundColor: 'white', borderRadius: 10, }, text: { fontSize: 20, color: 'black', }, }); ```
1 parent 0d87e2c commit cd27bfd

File tree

6 files changed

+35
-37
lines changed

6 files changed

+35
-37
lines changed

packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerDetectorView.kt

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) {
1313
private val reactContext: ThemedReactContext
1414
get() = context as ThemedReactContext
1515
private var handlersToAttach: List<Int>? = null
16-
private var nativeHandlersToAttach: MutableSet<Int> = mutableSetOf()
16+
private var nativeHandlers: MutableSet<Int> = mutableSetOf()
1717
private var attachedHandlers: MutableSet<Int> = mutableSetOf()
1818
private var moduleId: Int = -1
1919

@@ -48,7 +48,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) {
4848
override fun addView(child: View, index: Int, params: LayoutParams?) {
4949
super.addView(child, index, params)
5050

51-
tryAttachHandlerToChildView(child.id)
51+
tryAttachNativeHandlersToChildView(child.id)
5252
}
5353

5454
override fun removeViewAt(index: Int) {
@@ -75,51 +75,47 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) {
7575
val tag = entry.key
7676

7777
if (entry.value == GestureHandlerMutation.Attach) {
78-
// It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that case we cannot
79-
// attach `NativeViewGestureHandlers` here and we have to do it in `addView` method.
8078
if (shouldAttachGestureToChildView(tag)) {
81-
nativeHandlersToAttach.add(tag)
79+
// It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that case we cannot
80+
// attach `NativeViewGestureHandlers` here and we have to do it in `addView` method.
81+
nativeHandlers.add(tag)
8282
} else {
8383
registry.attachHandlerToView(tag, this.id, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR)
84-
8584
attachedHandlers.add(tag)
8685
}
8786
} else if (entry.value == GestureHandlerMutation.Detach) {
8887
registry.detachHandler(tag)
88+
nativeHandlers.remove(tag)
8989
attachedHandlers.remove(tag)
9090
}
9191
}
9292

93-
// This covers the case where `NativeViewGestureHandlers` are attached after child views were created.
9493
val child = getChildAt(0)
9594

95+
// This covers the case where `NativeViewGestureHandlers` are attached after child views were created.
9696
if (child != null) {
97-
tryAttachHandlerToChildView(child.id)
97+
tryAttachNativeHandlersToChildView(child.id)
9898
}
9999
}
100100

101-
private fun tryAttachHandlerToChildView(childId: Int) {
101+
private fun tryAttachNativeHandlersToChildView(childId: Int) {
102102
val registry = RNGestureHandlerModule.registries[moduleId]
103103
?: throw Exception("Tried to access a non-existent registry")
104104

105-
for (tag in nativeHandlersToAttach) {
105+
for (tag in nativeHandlers) {
106106
registry.attachHandlerToView(tag, childId, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR)
107107

108108
attachedHandlers.add(tag)
109109
}
110-
111-
nativeHandlersToAttach.clear()
112110
}
113111

114112
private fun detachNativeGestureHandlers() {
115113
val registry = RNGestureHandlerModule.registries[moduleId]
116114
?: throw Exception("Tried to access a non-existent registry")
117115

118-
for (tag in attachedHandlers) {
119-
if (shouldAttachGestureToChildView(tag)) {
120-
registry.detachHandler(tag)
121-
attachedHandlers.remove(tag)
122-
}
116+
for (tag in nativeHandlers) {
117+
registry.detachHandler(tag)
118+
attachedHandlers.remove(tag)
123119
}
124120
}
125121

packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
#endif
1414

1515
#import <React/RCTConvert.h>
16-
#import <React/UIView+React.h>
1716
#import <React/RCTScrollViewComponentView.h>
17+
#import <React/UIView+React.h>
1818

1919
#pragma mark RNDummyGestureRecognizer
2020

packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN
2020

2121
- (void)dispatchTouchEvent:(RNGestureHandlerDetectorEventEmitter::OnGestureHandlerTouchEvent)event;
2222

23-
- (void)tryAttachHandlerToChildView;
23+
- (void)tryAttachNativeHandlersToChildView;
2424

2525
- (void)detachNativeGestureHandlers;
2626

packages/react-native-gesture-handler/apple/RNGestureHandlerDetector.mm

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
@interface RNGestureHandlerDetector () <RCTRNGestureHandlerDetectorViewProtocol>
1515

16-
@property (nonatomic, nonnull) NSMutableSet *nativeHandlersToAttach;
16+
@property (nonatomic, nonnull) NSMutableSet *nativeHandlers;
1717
@property (nonatomic, nonnull) NSMutableSet *attachedHandlers;
1818

1919
@end
@@ -41,7 +41,7 @@ - (instancetype)initWithFrame:(CGRect)frame
4141
static const auto defaultProps = std::make_shared<const RNGestureHandlerDetectorProps>();
4242
_props = defaultProps;
4343
_moduleId = -1;
44-
_nativeHandlersToAttach = [NSMutableSet set];
44+
_nativeHandlers = [NSMutableSet set];
4545
_attachedHandlers = [NSMutableSet set];
4646
}
4747

@@ -94,11 +94,11 @@ - (BOOL)shouldAttachGestureToSubview:(NSNumber *)handlerTag
9494
return [[[handlerManager registry] handlerWithTag:handlerTag] wantsToAttachDirectlyToView];
9595
}
9696

97-
- (void)addSubview:(RNGHUIView *)view
97+
- (void)didAddSubview:(RNGHUIView *)view
9898
{
99-
[super addSubview:view];
99+
[super didAddSubview:view];
100100

101-
[self tryAttachHandlerToChildView];
101+
[self tryAttachNativeHandlersToChildView];
102102
}
103103

104104
- (void)willRemoveSubview:(RNGHUIView *)subview
@@ -152,7 +152,9 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar
152152

153153
if (handlerChange.second == RNGestureHandlerMutationAttach) {
154154
if ([self shouldAttachGestureToSubview:handlerTag]) {
155-
[_nativeHandlersToAttach addObject:handlerTag];
155+
// It might happen that `attachHandlers` will be called before children are added into view hierarchy. In that
156+
// case we cannot attach `NativeViewGestureHandlers` here and we have to do it in `didAddSubview` method.
157+
[_nativeHandlers addObject:handlerTag];
156158
} else {
157159
[handlerManager.registry attachHandlerWithTag:handlerTag
158160
toView:self
@@ -163,40 +165,40 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar
163165
} else if (handlerChange.second == RNGestureHandlerMutationDetach) {
164166
[handlerManager.registry detachHandlerWithTag:handlerTag];
165167
[_attachedHandlers removeObject:handlerTag];
168+
[_nativeHandlers removeObject:handlerTag];
166169
}
170+
}
167171

168-
[self tryAttachHandlerToChildView];
172+
// This covers the case where `NativeViewGestureHandlers` are attached after child views were created.
173+
if (!self.subviews[0]) {
174+
[self tryAttachNativeHandlersToChildView];
169175
}
170176

171177
[super updateProps:propsBase oldProps:oldPropsBase];
172178
// Override to force hittesting to work outside bounds
173179
self.clipsToBounds = NO;
174180
}
175181

176-
- (void)tryAttachHandlerToChildView
182+
- (void)tryAttachNativeHandlersToChildView
177183
{
178184
RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId];
179185

180-
for (NSNumber *handlerTag in _nativeHandlersToAttach) {
186+
for (NSNumber *handlerTag in _nativeHandlers) {
181187
[handlerManager.registry attachHandlerWithTag:handlerTag
182188
toView:self.subviews[0]
183189
withActionType:RNGestureHandlerActionTypeNativeDetector];
184190

185191
[_attachedHandlers addObject:handlerTag];
186192
}
187-
188-
[_nativeHandlersToAttach removeAllObjects];
189193
}
190194

191195
- (void)detachNativeGestureHandlers
192196
{
193197
RNGestureHandlerManager *handlerManager = [RNGestureHandlerModule handlerManagerForModuleId:_moduleId];
194198

195-
for (NSNumber *handlerTag in _attachedHandlers) {
196-
if ([self shouldAttachGestureToSubview:handlerTag]) {
197-
[[handlerManager registry] detachHandlerWithTag:handlerTag];
198-
[_attachedHandlers removeObject:handlerTag];
199-
}
199+
for (NSNumber *handlerTag in _nativeHandlers) {
200+
[[handlerManager registry] detachHandlerWithTag:handlerTag];
201+
[_attachedHandlers removeObject:handlerTag];
200202
}
201203
}
202204

packages/react-native-gesture-handler/apple/RNGestureHandlerModule.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
#import "RNGestureHandlerManager.h"
88

9-
@interface RNGestureHandlerModule : RCTEventEmitter<NativeRNGestureHandlerModuleSpec, RCTJSDispatcherModule, RCTInitializing>
9+
@interface RNGestureHandlerModule
10+
: RCTEventEmitter <NativeRNGestureHandlerModuleSpec, RCTJSDispatcherModule, RCTInitializing>
1011

1112
+ (RNGestureHandlerManager *)handlerManagerForModuleId:(int)moduleId;
1213

packages/react-native-gesture-handler/apple/RNGestureHandlerModule.mm

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ - (void)invalidate
7171
});
7272

7373
_managers[_moduleId] = nullptr;
74-
7574
}
7675

7776
- (dispatch_queue_t)methodQueue

0 commit comments

Comments
 (0)