Skip to content

Commit 728a895

Browse files
authored
Remove AnimatedEvent action type from NativeDetector (#3646)
## Description We came to a conclusion that current event handling may conflict with gesture relations, such as `simultaneous` when one of the gestures is using `Animated`. This PR removes `AnimatedEvent` action type from `NativeDetector` and adds flag to `GestureHandler`, which represents if handler should send events for `Animated`. ## Status - ### Android ✅ - ### iOS ✅ ## Test plan Test `pan1`, `pan2` and `gesture` from the code below. >[!NOTE] > Since `simultaneous` relation is not yet implemented, only first pan works when `gesture` is used. On Android you can solve that ba commenting out `otherHandler.cancel()` in `makeActive` function ([this line](https://github.com/software-mansion/react-native-gesture-handler/blob/0e5d58efcb2bd0ee2bd4eefa78fd366e003eea41/packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt#L205)) <details> <summary>Tested on the following code:</summary> ```tsx import * as React from 'react'; import { Animated, Button, useAnimatedValue } from 'react-native'; import { GestureHandlerRootView, NativeDetector, useGesture, } from 'react-native-gesture-handler'; export default function App() { const [visible, setVisible] = React.useState(true); const value = useAnimatedValue(0); const event = Animated.event( [{ nativeEvent: { handlerData: { translationX: value } } }], { useNativeDriver: true, } ); const pan1 = useGesture('PanGestureHandler', { onUpdate: (e: any) => { console.log('Pan1 update:', e); }, }); const pan2 = useGesture('PanGestureHandler', { onUpdate: event, dispatchesAnimatedEvents: true, }); const gesture = { tag: [pan1.tag, pan2.tag], gestureEvents: { onGestureHandlerStateChange: (e) => { pan1.gestureEvents.onGestureHandlerStateChange(e); pan2.gestureEvents.onGestureHandlerStateChange(e); }, onGestureHandlerEvent: pan1.gestureEvents.onGestureHandlerEvent, onGestureHandlerAnimatedEvent: pan2.gestureEvents.onGestureHandlerAnimatedEvent, onGestureHandlerTouchEvent: (e) => { pan1.gestureEvents.onGestureHandlerTouchEvent(e); pan2.gestureEvents.onGestureHandlerTouchEvent(e); }, }, dispatchesAnimatedEvents: true, // Assuming this is always true for this example }; return ( <GestureHandlerRootView style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}> <Button title="Toggle visibility" onPress={() => { setVisible(!visible); }} /> {visible && ( <NativeDetector gesture={gesture}> <Animated.View style={[ { width: 150, height: 150, backgroundColor: 'blue', opacity: 0.5, borderWidth: 10, borderColor: 'green', marginTop: 20, marginLeft: 40, }, { transform: [{ translateX: value }] }, ]} /> </NativeDetector> )} </GestureHandlerRootView> ); } ``` </details>
1 parent ff64e3f commit 728a895

13 files changed

+57
-66
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ open class GestureHandler {
6262
private set
6363
private val trackedPointers: Array<PointerData?> = Array(MAX_POINTERS_COUNT) { null }
6464
var needsPointerData = false
65+
var dispatchesAnimatedEvents = false
6566

6667
private var hitSlop: FloatArray? = null
6768
var eventCoalescingKey: Short = 0
@@ -859,6 +860,9 @@ open class GestureHandler {
859860
if (config.hasKey(KEY_NEEDS_POINTER_DATA)) {
860861
handler.needsPointerData = config.getBoolean(KEY_NEEDS_POINTER_DATA)
861862
}
863+
if (config.hasKey(KEY_DISPATCHES_ANIMATED_EVENTS)) {
864+
handler.dispatchesAnimatedEvents = config.getBoolean(KEY_DISPATCHES_ANIMATED_EVENTS)
865+
}
862866
if (config.hasKey(KEY_MANUAL_ACTIVATION)) {
863867
handler.manualActivation = config.getBoolean(KEY_MANUAL_ACTIVATION)
864868
}
@@ -873,6 +877,7 @@ open class GestureHandler {
873877
private const val KEY_SHOULD_CANCEL_WHEN_OUTSIDE = "shouldCancelWhenOutside"
874878
private const val KEY_ENABLED = "enabled"
875879
private const val KEY_NEEDS_POINTER_DATA = "needsPointerData"
880+
private const val KEY_DISPATCHES_ANIMATED_EVENTS = "dispatchesAnimatedEvents"
876881
private const val KEY_MANUAL_ACTIVATION = "manualActivation"
877882
private const val KEY_MOUSE_BUTTON = "mouseButton"
878883
private const val KEY_HIT_SLOP = "hitSlop"
@@ -968,7 +973,6 @@ open class GestureHandler {
968973
const val ACTION_TYPE_JS_FUNCTION_OLD_API = 3
969974
const val ACTION_TYPE_JS_FUNCTION_NEW_API = 4
970975
const val ACTION_TYPE_NATIVE_DETECTOR = 5
971-
const val ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT = 6
972976
const val POINTER_TYPE_TOUCH = 0
973977
const val POINTER_TYPE_STYLUS = 1
974978
const val POINTER_TYPE_MOUSE = 2

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,7 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) {
6060
registry.attachHandlerToView(
6161
entry.key,
6262
this.id,
63-
if (dispatchesAnimatedEvents) {
64-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
65-
} else {
66-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR
67-
},
63+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR,
6864
)
6965
} else if (entry.value == GestureHandlerMutation.Detach) {
7066
registry.detachHandler(entry.key)

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
1818
private var dataBuilder: GestureHandlerEventDataBuilder<*>? = null
1919
private var coalescingKey: Short = 0
2020
private var actionType: Int = GestureHandler.ACTION_TYPE_NATIVE_ANIMATED_EVENT
21+
private var useAnimatedEvent = false
2122

2223
// On the new architecture, native animated expects event names prefixed with `top` instead of `on`,
2324
// since we know when the native animated node is the target of the event we can use the different
@@ -37,6 +38,7 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
3738
this.actionType = actionType
3839
this.dataBuilder = dataBuilder
3940
this.useTopPrefixedName = useNativeAnimatedName
41+
this.useAnimatedEvent = useAnimatedEvent
4042
coalescingKey = handler.eventCoalescingKey
4143
}
4244

@@ -45,7 +47,7 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
4547
EVENTS_POOL.release(this)
4648
}
4749

48-
override fun getEventName() = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT) {
50+
override fun getEventName() = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR && useTopPrefixedName) {
4951
NATIVE_DETECTOR_ANIMATED_EVENT_NAME
5052
} else if (useTopPrefixedName) {
5153
NATIVE_ANIMATED_EVENT_NAME
@@ -57,9 +59,7 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
5759

5860
override fun getCoalescingKey() = coalescingKey
5961

60-
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR ||
61-
actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
62-
) {
62+
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) {
6363
createNativeEventData(dataBuilder!!)
6464
} else {
6565
createEventData(dataBuilder!!)

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

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -79,30 +79,21 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
7979
}
8080
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
8181
val view = handler.view
82+
8283
if (view is RNGestureHandlerDetectorView) {
83-
val event = RNGestureHandlerEvent.obtain(
84-
handler,
85-
handler.actionType,
86-
handlerFactory.createEventBuilder(handler),
87-
)
88-
view.dispatchEvent(event)
89-
}
90-
}
91-
// In case of a native detector with animated event listener, dispatch the event twice:
92-
// once for the animated event, second for any JS callbacks
93-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT -> {
94-
val view = handler.view
95-
if (view is RNGestureHandlerDetectorView) {
96-
val animatedEvent = RNGestureHandlerEvent.obtain(
97-
handler,
98-
handler.actionType,
99-
handlerFactory.createEventBuilder(handler),
100-
)
101-
view.dispatchEvent(animatedEvent)
84+
if (handler.dispatchesAnimatedEvents) {
85+
val animatedEvent = RNGestureHandlerEvent.obtain(
86+
handler,
87+
handler.actionType,
88+
handlerFactory.createEventBuilder(handler),
89+
true,
90+
)
91+
view.dispatchEvent(animatedEvent)
92+
}
10293

10394
val event = RNGestureHandlerEvent.obtain(
10495
handler,
105-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR,
96+
handler.actionType,
10697
handlerFactory.createEventBuilder(handler),
10798
)
10899
view.dispatchEvent(event)
@@ -164,7 +155,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
164155
sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data)
165156
}
166157

167-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT -> {
158+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
168159
val view = handler.view
169160
if (view is RNGestureHandlerDetectorView) {
170161
val event = RNGestureHandlerStateChangeEvent.obtain(
@@ -207,7 +198,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
207198
val data = RNGestureHandlerTouchEvent.createEventData(handler)
208199
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
209200
}
210-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT -> {
201+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
211202
val view = handler.view
212203
if (view is RNGestureHandlerDetectorView) {
213204
val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType)

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ class RNGestureHandlerStateChangeEvent private constructor() : Event<RNGestureHa
5050
// TODO: coalescing
5151
override fun getCoalescingKey(): Short = 0
5252

53-
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR ||
54-
actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
55-
) {
53+
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) {
5654
createNativeEventData(dataBuilder!!, newState, oldState)
5755
} else {
5856
createEventData(dataBuilder!!, newState, oldState)

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ class RNGestureHandlerTouchEvent private constructor() : Event<RNGestureHandlerT
2525
EVENTS_POOL.release(this)
2626
}
2727

28-
override fun getEventName() = if (actionType ==
29-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR ||
30-
actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
31-
) {
28+
override fun getEventName() = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) {
3229
NATIVE_EVENT_NAME
3330
} else {
3431
EVENT_NAME

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
- (void)sendEvent:(nonnull RNGestureHandlerStateChange *)event
4242
withActionType:(RNGestureHandlerActionType)actionType
43+
forAnimated:(BOOL)forAnimated
4344
forRecognizer:(UIGestureRecognizer *)recognizer;
4445

4546
- (void)sendNativeTouchEventForGestureHandler:(RNGestureHandler *)handler withPointerType:(NSInteger)pointerType;
@@ -75,6 +76,7 @@
7576
@property (nonatomic) BOOL shouldCancelWhenOutside;
7677
@property (nonatomic) BOOL needsPointerData;
7778
@property (nonatomic) BOOL manualActivation;
79+
@property (nonatomic) BOOL dispatchesAnimatedEvents;
7880

7981
#if RCT_NEW_ARCH_ENABLED
8082
- (BOOL)isViewParagraphComponent:(nullable RNGHUIView *)view;

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ - (void)configure:(NSDictionary *)config
146146
_needsPointerData = [RCTConvert BOOL:prop];
147147
}
148148

149+
prop = config[@"dispatchesAnimatedEvents"];
150+
if (prop != nil) {
151+
_dispatchesAnimatedEvents = [RCTConvert BOOL:prop];
152+
}
153+
149154
prop = config[@"manualActivation"];
150155
if (prop != nil) {
151156
self.manualActivation = [RCTConvert BOOL:prop];
@@ -287,8 +292,7 @@ - (void)handleGesture:(UIGestureRecognizer *)recognizer
287292
// it may happen that the gesture recognizer is reset after it's been unbound from the view,
288293
// it that recognizer tried to send event, the app would crash because the target of the event
289294
// would be nil.
290-
if (view.reactTag == nil && _actionType != RNGestureHandlerActionTypeNativeDetector &&
291-
_actionType != RNGestureHandlerActionTypeNativeDetectorAnimatedEvent) {
295+
if (view.reactTag == nil && _actionType != RNGestureHandlerActionTypeNativeDetector) {
292296
return;
293297
}
294298

@@ -303,9 +307,7 @@ - (void)handleGesture:(UIGestureRecognizer *)recognizer inState:(RNGestureHandle
303307
RNGestureHandlerEventExtraData *eventData = [self eventExtraData:recognizer];
304308

305309
NSNumber *tag = [self chooseViewForInteraction:recognizer].reactTag;
306-
if (tag == nil &&
307-
(_actionType == RNGestureHandlerActionTypeNativeDetector ||
308-
_actionType == RNGestureHandlerActionTypeNativeDetectorAnimatedEvent)) {
310+
if (tag == nil && _actionType == RNGestureHandlerActionTypeNativeDetector) {
309311
tag = @(recognizer.view.tag);
310312
}
311313

@@ -363,15 +365,18 @@ - (void)sendEventsInState:(RNGestureHandlerState)state
363365
handlerTag:_tag
364366
state:state
365367
extraData:extraData
366-
forActionType:_actionType
368+
forAnimated:_dispatchesAnimatedEvents
367369
coalescingKey:self->_eventCoalescingKey];
368370
[self sendEvent:touchEvent];
369371
}
370372
}
371373

372374
- (void)sendEvent:(RNGestureHandlerStateChange *)event
373375
{
374-
[self.emitter sendEvent:event withActionType:self.actionType forRecognizer:self.recognizer];
376+
[self.emitter sendEvent:event
377+
withActionType:self.actionType
378+
forAnimated:_dispatchesAnimatedEvents
379+
forRecognizer:self.recognizer];
375380
}
376381

377382
- (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(NSNumber *)reactTag
@@ -388,10 +393,13 @@ - (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(NSNum
388393
handlerTag:_tag
389394
state:state
390395
extraData:extraData
391-
forActionType:_actionType
396+
forAnimated:_dispatchesAnimatedEvents
392397
coalescingKey:[_tag intValue]];
393398

394-
[self.emitter sendEvent:event withActionType:self.actionType forRecognizer:self.recognizer];
399+
[self.emitter sendEvent:event
400+
withActionType:self.actionType
401+
forAnimated:_dispatchesAnimatedEvents
402+
forRecognizer:self.recognizer];
395403
}
396404
}
397405

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@ typedef NS_ENUM(NSInteger, RNGestureHandlerActionType) {
88
RNGestureHandlerActionTypeJSFunctionNewAPI, // JS function or Animated.event with useNativeDriver: false using new
99
// RNGH API
1010
RNGestureHandlerActionTypeNativeDetector,
11-
RNGestureHandlerActionTypeNativeDetectorAnimatedEvent,
1211
};

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,9 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar
125125

126126
if (handlerChange.second == RNGestureHandlerMutationAttach) {
127127
// TODO: Attach to the child when attached gesture is a NativeGestureHandler, track children changes then
128-
[handlerManager.registry
129-
attachHandlerWithTag:handlerTag
130-
toView:self
131-
withActionType:newProps.dispatchesAnimatedEvents ? RNGestureHandlerActionTypeNativeDetectorAnimatedEvent
132-
: RNGestureHandlerActionTypeNativeDetector];
128+
[handlerManager.registry attachHandlerWithTag:handlerTag
129+
toView:self
130+
withActionType:RNGestureHandlerActionTypeNativeDetector];
133131
} else if (handlerChange.second == RNGestureHandlerMutationDetach) {
134132
[handlerManager.registry detachHandlerWithTag:handlerTag];
135133
}

0 commit comments

Comments
 (0)