Skip to content

Commit dcdfe90

Browse files
committed
Support animated events
1 parent 67fac20 commit dcdfe90

18 files changed

+246
-60
lines changed

apps/basic-example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2678,7 +2678,7 @@ SPEC CHECKSUMS:
26782678
RNReanimated: 25060745a200605462ff56cf488411db066631ce
26792679
RNWorklets: 9bb08cb0ef718ce063f61ca18f95f57aec9b9673
26802680
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
2681-
Yoga: 0c4b7d2aacc910a1f702694fa86be830386f4ceb
2681+
Yoga: 395b5d614cd7cbbfd76b05d01bd67230a6ad004e
26822682

26832683
PODFILE CHECKSUM: d05778d3a61b8d49242579ea0aa864580fbb1f64
26842684

apps/basic-example/src/App.tsx

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
/* eslint-disable react-native/no-inline-styles */
21
import * as React from 'react';
3-
import { Button, View } from 'react-native';
2+
import { Animated, Button, useAnimatedValue } from 'react-native';
43
import {
54
GestureHandlerRootView,
65
NativeDetector,
@@ -9,8 +8,26 @@ import {
98

109
export default function App() {
1110
const [visible, setVisible] = React.useState(true);
12-
const tap = useGesture('TapGestureHandler', {
13-
numberOfTaps: 2,
11+
const [animate, setAnimate] = React.useState(true);
12+
13+
const value = useAnimatedValue(0);
14+
const event = Animated.event(
15+
[{ nativeEvent: { handlerData: { translationX: value } } }],
16+
{
17+
useNativeDriver: true,
18+
}
19+
);
20+
21+
const tap = useGesture('PanGestureHandler', {
22+
// numberOfTaps: 2,
23+
// needsPointerData: true,
24+
// onGestureHandlerStateChange: (event) => {
25+
// console.log('onHandlerStateChange', event.nativeEvent);
26+
// },
27+
onGestureHandlerEvent: animate
28+
? event
29+
: (e: any) => console.log('onGestureHandlerEvent', e),
30+
// onGestureHandlerTouchEvent: (event) => console.log('onGestureTouchEvent', event.nativeEvent),
1431
});
1532

1633
return (
@@ -22,23 +39,93 @@ export default function App() {
2239
setVisible(!visible);
2340
}}
2441
/>
42+
<Button
43+
title="Toggle animate"
44+
onPress={() => {
45+
setAnimate(!animate);
46+
}}
47+
/>
2548

2649
{visible && (
2750
<NativeDetector gesture={tap}>
28-
<View
29-
style={{
30-
width: 150,
31-
height: 150,
32-
backgroundColor: 'blue',
33-
opacity: 0.5,
34-
borderWidth: 10,
35-
borderColor: 'green',
36-
marginTop: 20,
37-
marginLeft: 40,
38-
}}
51+
<Animated.View
52+
style={[
53+
{
54+
width: 150,
55+
height: 150,
56+
backgroundColor: 'blue',
57+
opacity: 0.5,
58+
borderWidth: 10,
59+
borderColor: 'green',
60+
marginTop: 20,
61+
marginLeft: 40,
62+
},
63+
{ transform: [{ translateX: value }] },
64+
]}
3965
/>
4066
</NativeDetector>
4167
)}
4268
</GestureHandlerRootView>
4369
);
4470
}
71+
72+
// import * as React from 'react';
73+
// import { Button } from 'react-native';
74+
// import {
75+
// GestureHandlerRootView,
76+
// NativeDetector,
77+
// useGesture,
78+
// } from 'react-native-gesture-handler';
79+
// import Animated, { useEvent } from 'react-native-reanimated';
80+
81+
// export default function App() {
82+
// const [visible, setVisible] = React.useState(true);
83+
84+
// function handler(event) {
85+
// 'worklet';
86+
// console.log('onGestureHandlerEvent', _WORKLET);
87+
// }
88+
89+
// const event = useEvent(handler, ['onGestureHandlerEvent']);
90+
91+
// const tap = useGesture('PanGestureHandler', {
92+
// numberOfTaps: 2,
93+
// // needsPointerData: true,
94+
// // onGestureHandlerStateChange: (event) => {
95+
// // console.log('onHandlerStateChange', event.nativeEvent);
96+
// // },
97+
// onGestureHandlerEvent: event,
98+
// // onGestureHandlerTouchEvent: (event) => console.log('onGestureTouchEvent', event.nativeEvent),
99+
// });
100+
101+
// return (
102+
// <GestureHandlerRootView
103+
// style={{ flex: 1, backgroundColor: 'white', paddingTop: 150 }}>
104+
// <Button
105+
// title="Toggle"
106+
// onPress={() => {
107+
// setVisible(!visible);
108+
// }}
109+
// />
110+
111+
// {visible && (
112+
// <NativeDetector gesture={tap}>
113+
// <Animated.View
114+
// style={[
115+
// {
116+
// width: 150,
117+
// height: 150,
118+
// backgroundColor: 'blue',
119+
// opacity: 0.5,
120+
// borderWidth: 10,
121+
// borderColor: 'green',
122+
// marginTop: 20,
123+
// marginLeft: 40,
124+
// },
125+
// ]}
126+
// />
127+
// </NativeDetector>
128+
// )}
129+
// </GestureHandlerRootView>
130+
// );
131+
// }

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ open class GestureHandler {
962962
const val ACTION_TYPE_JS_FUNCTION_OLD_API = 3
963963
const val ACTION_TYPE_JS_FUNCTION_NEW_API = 4
964964
const val ACTION_TYPE_NATIVE_DETECTOR = 5
965+
const val ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT = 6
965966
const val POINTER_TYPE_TOUCH = 0
966967
const val POINTER_TYPE_STYLUS = 1
967968
const val POINTER_TYPE_MOUSE = 2

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

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,37 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) {
1313
get() = context as ThemedReactContext
1414
private var attachedHandlers = listOf<Int>()
1515
private var moduleId: Int = -1
16+
private var animatedEvents: Boolean = false
1617

1718
fun setHandlerTags(handlerTags: ReadableArray?) {
19+
val newHandlers = handlerTags?.toArrayList()?.map { (it as Double).toInt() } ?: emptyList()
20+
if (moduleId == -1) {
21+
attachedHandlers = newHandlers
22+
return
23+
}
24+
25+
attachHandlers(newHandlers)
26+
}
27+
28+
fun setModuleId(id: Int) {
29+
if (this.moduleId == -1) {
30+
this.moduleId = id
31+
val handlersToAttach = this.attachedHandlers
32+
this.attachedHandlers = emptyList()
33+
this.attachHandlers(handlersToAttach)
34+
} else {
35+
throw Exception("Tried to change moduleId of a native detector")
36+
}
37+
}
38+
39+
fun setAnimatedEvents(animatedEvents: Boolean) {
40+
this.animatedEvents = animatedEvents
41+
}
42+
43+
private fun attachHandlers(newHandlers: List<Int>) {
1844
val registry = RNGestureHandlerModule.registries[moduleId]
1945
?: throw Exception("Tried to access a non-existent registry")
2046

21-
val newHandlers = handlerTags?.toArrayList()?.map { (it as Double).toInt() } ?: emptyList()
22-
2347
val keep = 0
2448
val attach = 1
2549
val detach = 2
@@ -37,20 +61,20 @@ class RNGestureHandlerDetectorView(context: Context) : ReactViewGroup(context) {
3761
for (entry in changes) {
3862
if (entry.value == attach) {
3963
registry.attachHandlerToView(
40-
entry.value,
64+
entry.key,
4165
this.id,
42-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR,
66+
if (animatedEvents) {
67+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
68+
} else {
69+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR
70+
},
4371
)
4472
} else if (entry.value == detach) {
4573
registry.detachHandler(entry.value)
4674
}
4775
}
4876
}
4977

50-
fun setModuleId(id: Int) {
51-
this.moduleId = id
52-
}
53-
5478
fun dispatchEvent(event: Event<*>) {
5579
val eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
5680
eventDispatcher?.dispatchEvent(event)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,8 @@ class RNGestureHandlerDetectorViewManager :
3636
override fun setModuleId(view: RNGestureHandlerDetectorView, value: Int) {
3737
view.setModuleId(value)
3838
}
39+
40+
override fun setAnimatedEvents(view: RNGestureHandlerDetectorView, value: Boolean) {
41+
view.setAnimatedEvents(value)
42+
}
3943
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ class RNGestureHandlerEvent private constructor() : Event<RNGestureHandlerEvent>
5555

5656
override fun getCoalescingKey() = coalescingKey
5757

58-
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR) {
58+
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR ||
59+
actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
60+
) {
5961
createNativeEventData(dataBuilder!!)
6062
} else {
6163
createEventData(dataBuilder!!)

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,14 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
7777
RNGestureHandlerEvent.createEventData(handlerFactory.createEventBuilder(handler))
7878
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
7979
}
80-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
80+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT -> {
8181
val view = handler.view
8282
if (view is RNGestureHandlerDetectorView) {
8383
val event = RNGestureHandlerEvent.obtain(
8484
handler,
8585
handler.actionType,
8686
handlerFactory.createEventBuilder(handler),
87+
useTopPrefixedName = handler.actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT,
8788
)
8889
view.dispatchEvent(event)
8990
}
@@ -144,7 +145,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
144145
sendEventForDeviceEvent(RNGestureHandlerStateChangeEvent.EVENT_NAME, data)
145146
}
146147

147-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
148+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT -> {
148149
val view = handler.view
149150
if (view is RNGestureHandlerDetectorView) {
150151
val event = RNGestureHandlerStateChangeEvent.obtain(
@@ -187,7 +188,7 @@ class RNGestureHandlerEventDispatcher(private val reactApplicationContext: React
187188
val data = RNGestureHandlerTouchEvent.createEventData(handler)
188189
sendEventForDeviceEvent(RNGestureHandlerEvent.EVENT_NAME, data)
189190
}
190-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR -> {
191+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR, GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT -> {
191192
val view = handler.view
192193
if (view is RNGestureHandlerDetectorView) {
193194
val event = RNGestureHandlerTouchEvent.obtain(handler, handler.actionType)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ 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) {
53+
override fun getEventData(): WritableMap = if (actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR ||
54+
actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
55+
) {
5456
createNativeEventData(dataBuilder!!, newState, oldState)
5557
} else {
5658
createEventData(dataBuilder!!, newState, oldState)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class RNGestureHandlerTouchEvent private constructor() : Event<RNGestureHandlerT
2626
}
2727

2828
override fun getEventName() = if (actionType ==
29-
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR
29+
GestureHandler.ACTION_TYPE_NATIVE_DETECTOR ||
30+
actionType == GestureHandler.ACTION_TYPE_NATIVE_DETECTOR_ANIMATED_EVENT
3031
) {
3132
NATIVE_EVENT_NAME
3233
} else {

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ - (void)handleGesture:(UIGestureRecognizer *)recognizer
254254
// it may happen that the gesture recognizer is reset after it's been unbound from the view,
255255
// it that recognizer tried to send event, the app would crash because the target of the event
256256
// would be nil.
257-
if (recognizer.view.reactTag == nil && _actionType != RNGestureHandlerActionTypeNativeDetector) {
257+
if (recognizer.view.reactTag == nil && _actionType != RNGestureHandlerActionTypeNativeDetector &&
258+
_actionType != RNGestureHandlerActionTypeNativeDetectorAnimatedEvent) {
258259
return;
259260
}
260261

@@ -267,7 +268,14 @@ - (void)handleGesture:(UIGestureRecognizer *)recognizer inState:(RNGestureHandle
267268
_state = state;
268269

269270
RNGestureHandlerEventExtraData *eventData = [self eventExtraData:recognizer];
270-
[self sendEventsInState:self.state forViewWithTag:recognizer.view.reactTag withExtraData:eventData];
271+
272+
// TODO: do this only for native detector?
273+
NSNumber *tag = recognizer.view.reactTag;
274+
if (tag == nil) {
275+
tag = @(recognizer.view.tag);
276+
}
277+
278+
[self sendEventsInState:self.state forViewWithTag:tag withExtraData:eventData];
271279
}
272280

273281
- (void)sendEventsInState:(RNGestureHandlerState)state
@@ -321,6 +329,7 @@ - (void)sendEventsInState:(RNGestureHandlerState)state
321329
handlerTag:_tag
322330
state:state
323331
extraData:extraData
332+
forActionType:_actionType
324333
coalescingKey:self->_eventCoalescingKey];
325334
[self sendEvent:touchEvent];
326335
}
@@ -345,6 +354,7 @@ - (void)sendTouchEventInState:(RNGestureHandlerState)state forViewWithTag:(NSNum
345354
handlerTag:_tag
346355
state:state
347356
extraData:extraData
357+
forActionType:_actionType
348358
coalescingKey:[_tag intValue]];
349359

350360
[self.emitter sendEvent:event withActionType:self.actionType forRecognizer:self.recognizer];

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

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

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ + (ComponentDescriptorProvider)componentDescriptorProvider
9494
return concreteComponentDescriptorProvider<RNGestureHandlerDetectorComponentDescriptor>();
9595
}
9696

97+
- (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics
98+
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics
99+
{
100+
auto newLayoutMetrics = layoutMetrics;
101+
// Override to force hittesting to work outside bounds
102+
newLayoutMetrics.overflowInset = {.left = 1, .right = 1, .top = 1, .bottom = 1};
103+
104+
[super updateLayoutMetrics:newLayoutMetrics oldLayoutMetrics:oldLayoutMetrics];
105+
}
106+
97107
- (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shared &)oldPropsBase
98108
{
99109
const auto &newProps = *std::static_pointer_cast<const RNGestureHandlerDetectorProps>(propsBase);
@@ -120,18 +130,21 @@ - (void)updateProps:(const Props::Shared &)propsBase oldProps:(const Props::Shar
120130
for (const auto handlerChange : changes) {
121131
NSNumber *handlerTag = [NSNumber numberWithInt:handlerChange.first];
122132

123-
// TODO: Do this better than exposing handlerManager as a static property
124133
if (handlerChange.second == ATTACH) {
125134
// TODO: Attach to the child when NativeGestureHandler, track children changes?
126-
[handlerManager.registry attachHandlerWithTag:handlerTag
127-
toView:self
128-
withActionType:RNGestureHandlerActionTypeNativeDetector];
135+
[handlerManager.registry
136+
attachHandlerWithTag:handlerTag
137+
toView:self
138+
withActionType:newProps.animatedEvents ? RNGestureHandlerActionTypeNativeDetectorAnimatedEvent
139+
: RNGestureHandlerActionTypeNativeDetector];
129140
} else if (handlerChange.second == DROP) {
130141
[handlerManager.registry detachHandlerWithTag:handlerTag];
131142
}
132143
}
133144

134145
[super updateProps:propsBase oldProps:oldPropsBase];
146+
// Override to force hittesting to work outside bounds
147+
self.clipsToBounds = NO;
135148
}
136149
@end
137150

0 commit comments

Comments
 (0)