-
-
Notifications
You must be signed in to change notification settings - Fork 575
feat(iOS): Handle interactiveContentPopGesture for iOS 26 #3173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(iOS): Handle interactiveContentPopGesture for iOS 26 #3173
Conversation
9a80b45
to
468b1fe
Compare
ios/RNSScreen.mm
Outdated
if (@available(iOS 26, *)) { | ||
// Reenable interactions, see viewWillAppear | ||
[self findReactRootViewController].view.userInteractionEnabled = true; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking, is it possible that viewWillAppear
will run but viewDidAppear
will not? Did you check how prevent remove
works now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this on Test2125 which demos preventRemove
When using back button with prevent remove, the calls look like this:
We are covered by SecondScreen going back on top
When using swipe and not dismissing fully:
It's the same
When using swipe and actually dismissing SecondScreen (preventRemove still on):
Which is different ( 🤔 ) but works for our case
and without preventRemove it's like this:
so in every case at least one of the screens goes through the whole process
08ccb30
to
267ff70
Compare
…during transition
267ff70
to
852ed1f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also check behavior with stack nested in modal, as it is attached directly to UIWindow instead of react root view (@WoLewicki's suggestion).
ios/RNSScreen.mm
Outdated
// Furthermore, a stack put inside a modal will exist in an entirely different hierarchy | ||
// To be sure, we block interactions on the whole window | ||
// We need to get the window instance from parent view because it is not set until the view has appeared | ||
self.parentViewController.view.window.userInteractionEnabled = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please check one more thing - when you have some push screens and opened modal, when you dismiss the modal and quickly click the back button, will it work correctly (I'm not sure if viewWillAppear
will be called)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And another thing - when I tested main (previous solution), this happened:
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2025-09-04.at.10.44.06.mov
Please check if this solution prevents this but I managed to also reproduce the bug on iOS 18. If this isn't fixed by this PR, let's create a ticket to investigate this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you dismiss the modal and quickly click the back button
It actually broke again in situation: stack1_screenA / stack2_screen A / stack2_screen B / stack2_modal C; when quickly dismissing modal and screen, the whole stack was dismissed. I managed to fix that, but it's not pretty. The main challenge was to find a reference to correct window without using sharedApplication.
As for dismissing multiple modals, native UIKit prevents dismissing more than 2 at once, blocking the third one mid swipe. I ended up blocking every consecutive dismissal similar to regular screens.
And another thing - when I tested main (previous solution), this happened:
I didn't notice anything on my PR
Paper appears to work, too. One other thing I found is that when UIWindow has interactions disabled and we still try to input gestures, this log is shown
I suspect that's because there is nothing above the window to handle the gesture, but I don't know really. There is not that much info online, other that people having this error when app breaks with black screen, which doesn't happen here. |
Description
Resolves https://github.com/software-mansion/react-native-screens-labs/issues/369, might resolve #3161, reverts #3141, #3142
This PR attempts to enable interactiveContentPopGestureRecognizer for iOS 26 to achieve native screen popping behavior. Until 26, the default was to swipe from the edge of the screen. We had the option to do fullscreen switch, which was controlled by a
fullScreenSwipeEnabled
prop. Since the default behavior has changed, this prop, along withgestureResponseDistance
, is being ignored from now on.New iOS allows for popping multiple screens almost at once, which we still cannot support due to asynchronous nature of stack updates coming from host to JS that would create a "feedback loop" in situation like the following: host pops 1. screen + sends update, pops 2. screen + sends update -> JS acknowledges 1. update + sends updated state -> host gets 2. screen from JS and pushes it again. This PR attempts to block more than 1 pop at once by removing interactions from the whole screen. As a (desired) side effect, this also disables interactions on the screen below the one that is popped until the transition finishes.
Changes
RNSPanGestureRecognizer
from iOS 26 build and replace it with nativeinteractiveContentPopGestureRecognizer
Test code and steps to reproduce
Use Test3173 to test swipe and interactions on bare screens API & compare with any other test that uses react-navigation stack, i.e Test3093. Use Test3093 with additional screenOptions:
to test custom animations on swipe back.