diff --git a/packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm b/packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm index da03a2565e..d54be9db61 100644 --- a/packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm +++ b/packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm @@ -253,11 +253,6 @@ - (void)handleTouchDown:(UIView *)sender forEvent:(UIEvent *)event withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES withNumberOfTouches:event.allTouches.count withPointerType:_pointerType]]; - - [self sendActiveStateEventIfChangedForView:sender - extraData:[RNGestureHandlerEventExtraData forPointerInside:YES - withNumberOfTouches:event.allTouches.count - withPointerType:_pointerType]]; } - (void)handleTouchUpOutside:(UIView *)sender forEvent:(UIEvent *)event @@ -275,11 +270,12 @@ - (void)handleTouchUpOutside:(UIView *)sender forEvent:(UIEvent *)event - (void)handleTouchUpInside:(UIView *)sender forEvent:(UIEvent *)event { - [self sendEventsInState:RNGestureHandlerStateEnd - forViewWithTag:sender.reactTag - withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES - withNumberOfTouches:event.allTouches.count - withPointerType:_pointerType]]; + RNGestureHandlerEventExtraData *extraData = [RNGestureHandlerEventExtraData forPointerInside:YES + withNumberOfTouches:event.allTouches.count + withPointerType:_pointerType]; + + [self sendActiveStateEventIfChangedForView:sender extraData:extraData]; + [self sendEventsInState:RNGestureHandlerStateEnd forViewWithTag:sender.reactTag withExtraData:extraData]; } - (void)handleDragExit:(UIView *)sender forEvent:(UIEvent *)event diff --git a/packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx b/packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx index e579863201..aba80c8a8f 100644 --- a/packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx +++ b/packages/react-native-gesture-handler/src/__tests__/api_v3.test.tsx @@ -134,7 +134,7 @@ describe('[API v3] Components', () => { >; const { jsEventHandler } = gesture.detectorCallbacks; - // Fire BEGAN + // Fire BEGAN — long press timer starts here act(() => { jsEventHandler?.({ oldState: State.UNDETERMINED, @@ -145,7 +145,7 @@ describe('[API v3] Components', () => { }); }); - // Fire ACTIVE — long press timer starts here (on iOS / non-Android) + // Fire ACTIVE act(() => { jsEventHandler?.({ oldState: State.BEGAN, diff --git a/packages/react-native-gesture-handler/src/components/GestureButtons.tsx b/packages/react-native-gesture-handler/src/components/GestureButtons.tsx index 151e2da48c..aeef28dca5 100644 --- a/packages/react-native-gesture-handler/src/components/GestureButtons.tsx +++ b/packages/react-native-gesture-handler/src/components/GestureButtons.tsx @@ -36,13 +36,13 @@ class InnerBaseButton extends React.Component { delayLongPress: 600, }; - private lastActive: boolean; + private lastIsPressed: boolean; private longPressTimeout: ReturnType | undefined; private longPressDetected: boolean; constructor(props: BaseButtonWithRefProps) { super(props); - this.lastActive = false; + this.lastIsPressed = false; this.longPressDetected = false; } @@ -50,23 +50,24 @@ class InnerBaseButton extends React.Component { nativeEvent, }: HandlerStateChangeEvent) => { const { state, oldState, pointerInside } = nativeEvent; - const active = pointerInside && state === State.ACTIVE; + const isPressed = + pointerInside && (state === State.BEGAN || state === State.ACTIVE); - if (active !== this.lastActive && this.props.onActiveStateChange) { - this.props.onActiveStateChange(active); + if (isPressed !== this.lastIsPressed && this.props.onActiveStateChange) { + this.props.onActiveStateChange(isPressed); } if ( !this.longPressDetected && oldState === State.ACTIVE && state !== State.CANCELLED && - this.lastActive && + this.lastIsPressed && this.props.onPress ) { this.props.onPress(pointerInside); } - if (!this.lastActive && state === State.BEGAN && pointerInside) { + if (!this.lastIsPressed && state === State.BEGAN && pointerInside) { this.longPressDetected = false; if (this.props.onLongPress) { this.longPressTimeout = setTimeout( @@ -93,7 +94,7 @@ class InnerBaseButton extends React.Component { this.longPressTimeout = undefined; } - this.lastActive = active; + this.lastIsPressed = isPressed; }; private onLongPress = () => { diff --git a/packages/react-native-gesture-handler/src/components/touchables/GenericTouchable.tsx b/packages/react-native-gesture-handler/src/components/touchables/GenericTouchable.tsx index 22dfd99654..5fde5c90f2 100644 --- a/packages/react-native-gesture-handler/src/components/touchables/GenericTouchable.tsx +++ b/packages/react-native-gesture-handler/src/components/touchables/GenericTouchable.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { Component } from 'react'; -import { Animated, Platform } from 'react-native'; +import { Animated } from 'react-native'; import type { GestureEvent, @@ -174,10 +174,7 @@ export default class GenericTouchable extends Component< // Need to handle case with external cancellation (e.g. by ScrollView) this.moveToState(TOUCHABLE_STATE.UNDETERMINED); } else if ( - // This platform check is an implication of slightly different behavior of handlers on different platform. - // And Android "Active" state is achieving on first move of a finger, not on press in. - // On iOS event on "Began" is not delivered. - state === (Platform.OS !== 'android' ? State.ACTIVE : State.BEGAN) && + state === State.BEGAN && this.STATE === TOUCHABLE_STATE.UNDETERMINED ) { // Moving inside requires diff --git a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx index fb9caab3b3..b7e088b39e 100644 --- a/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx +++ b/packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx @@ -69,16 +69,16 @@ export const BaseButton = (props: BaseButtonProps) => { }; const onDeactivate = (e: EndCallbackEventType) => { + props.onDeactivate?.(e); + }; + + const onFinalize = (e: EndCallbackEventType) => { onActiveStateChange?.(false); if (!e.canceled && !longPressDetected.current) { onPress?.(e.pointerInside); } - props.onDeactivate?.(e); - }; - - const onFinalize = (e: EndCallbackEventType) => { if (longPressTimeout.current !== undefined) { clearTimeout(longPressTimeout.current); longPressTimeout.current = undefined;