Skip to content

[iOS] Change when Native gesture states are changed#4036

Open
m-bert wants to merge 8 commits intomainfrom
@mbert/unify-native-handler-callbacks
Open

[iOS] Change when Native gesture states are changed#4036
m-bert wants to merge 8 commits intomainfrom
@mbert/unify-native-handler-callbacks

Conversation

@m-bert
Copy link
Contributor

@m-bert m-bert commented Mar 23, 2026

Description

Native gesture is specific and its behavior differs across platforms. This leads to strange workarounds in our codebase (e.g. buttons).

In this PR I'm changing how events are sent on native side on iOS, such that it is closer to Android behavior.

Test plan

Tested on expo-example app (buttons / Pressable)

Copilot AI review requested due to automatic review settings March 23, 2026 09:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adjusts iOS Native gesture event/state emission to better align with Android behavior, reducing platform-specific workarounds in JS components (notably Pressable/buttons).

Changes:

  • Update v3 Pressable to avoid using Native.onActivate on iOS and to always run finalize cleanup.
  • Simplify v3 GestureButtons long-press scheduling to consistently start from onBegin (removing platform branching).
  • Modify iOS RNNativeViewHandler to emit BEGAN on touch-down and to forward additional drag-inside/outside pointer updates; update Pressable iOS state machine config accordingly.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
packages/react-native-gesture-handler/src/v3/components/Pressable.tsx Adjusts iOS handling to rely on onBegin/touch-down tracking instead of onActivate, and unifies finalize cleanup.
packages/react-native-gesture-handler/src/v3/components/GestureButtons.tsx Removes platform-specific workaround by starting long-press logic from onBegin for all platforms.
packages/react-native-gesture-handler/src/components/Pressable/stateDefinitions.ts Switches iOS Pressable state machine to trigger handlePressIn on NATIVE_BEGIN instead of NATIVE_START.
packages/react-native-gesture-handler/apple/Handlers/RNNativeViewHandler.mm Changes iOS UIControl-based native handler state emission (touch-down now BEGAN) and adds drag-inside/outside event forwarding.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +193 to 196
[self sendEventsInState:RNGestureHandlerStateBegan
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
withNumberOfTouches:event.allTouches.count
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleTouchDown now emits RNGestureHandlerStateBegan instead of ...StateActive. With the current JS button implementations (both legacy src/components/GestureButtons.tsx and v3 src/v3/components/GestureButtons.tsx), the “pressed/active” UI feedback is driven by the gesture entering ACTIVE while the finger is down. If iOS stays in BEGAN until drag/finish, onActiveStateChange(true)/pressed underlay/opacity won’t update on touch down (it may only flash at release due to the synthetic ACTIVE sent right before END in sendEventsInState). If the intent is to start sending BEGAN for parity but keep pressed feedback, consider sending BEGAN and then immediately ACTIVE on touch down (or otherwise ensuring iOS enters ACTIVE at press start).

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-bert m-bert marked this pull request as ready for review March 23, 2026 11:52
@m-bert m-bert requested a review from j-piasecki March 23, 2026 11:52
Comment on lines +149 to +154
[control addTarget:self
action:@selector(handleDragInside:forEvent:)
forControlEvents:UIControlEventTouchDragInside];
[control addTarget:self
action:@selector(handleDragOutside:forEvent:)
forControlEvents:UIControlEventTouchDragOutside];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this means that ios buttons will also spam events when pointer moves when pressed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically yes.

Comment on lines +193 to 195
[self sendEventsInState:RNGestureHandlerStateBegan
forViewWithTag:sender.reactTag
withExtraData:[RNGestureHandlerEventExtraData forPointerInside:YES
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe the "correct" approach would be to send begin and active immediately when pressed down (and do the same on other platforms)?

This may be weird that the button isn't "active" unless the pointer moves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also thought about it. This would also match current web logic.

if (onLongPress) {
longPressTimeout.current = setTimeout(wrappedLongPress, delayLongPress);
}
// iOS, macOS. Web has its own implementation of button.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove the pointerInside check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

props.onBegin?.(e);
if (Platform.OS === 'android') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-bert m-bert requested a review from j-piasecki March 24, 2026 15:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants