Skip to content

feat(iOS, FormSheet v5): Add support for preventNativeDismiss & onNativeDismissPrevented#4022

Open
t0maboro wants to merge 3 commits into
mainfrom
@t0maboro/form-sheet-prevent-native-dismiss
Open

feat(iOS, FormSheet v5): Add support for preventNativeDismiss & onNativeDismissPrevented#4022
t0maboro wants to merge 3 commits into
mainfrom
@t0maboro/form-sheet-prevent-native-dismiss

Conversation

@t0maboro
Copy link
Copy Markdown
Contributor

@t0maboro t0maboro commented May 13, 2026

Description

Adds preventNativeDismiss support. When preventNativeDismiss is true, the user cannot dismiss the sheet natively via swipe-down or by tapping the backdrop. Programmatic dismissal from JS still works.
When dismissal is prevented, the sheet emits a new onNativeDismissPrevented event so JS can react.

Note

Backdrop taps need extra handling because UIKit doesn't fire delegate callbacks for this path. I follow the same approach as in #3771 with attaching a UITapGestureRecognizer to the presentation controller's containerView and filter touches, so only taps outside presentedView are recognized. The recognizer is attached/detached in viewWillAppear/viewDidDisappear, because UIKit recreates the container view on every present cycle.

Important

I know that this implementation is messy, so we may wait before landing this PR to proceed with the refactor we discussed internally, or merge it and the adapt together with other props.

Closes: https://github.com/software-mansion/react-native-screens-labs/issues/1272

Changes

  • preventNativeDismiss prop
  • onNativeDismissPrevented event
  • attached a UITapGestureRecognizer for backdrop taps.
  • overriden delegate methods: presentationControllerShouldDismiss , presentationControllerDidAttemptToDismiss

Visual documentation

preventnativedismiss.mov

@t0maboro t0maboro changed the base branch from main to @t0maboro/formsheet-modal-v5 May 13, 2026 08:09
@t0maboro t0maboro requested a review from Copilot May 13, 2026 08:09
@t0maboro t0maboro marked this pull request as ready for review May 13, 2026 08:10
Copy link
Copy Markdown
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 extends the experimental Gamma FormSheet (Fabric/iOS) implementation with a preventNativeDismiss prop to block user-initiated dismiss (swipe-down + backdrop tap), and introduces an onNativeDismissPrevented event so JS can react to blocked dismissal attempts.

Changes:

  • Added preventNativeDismiss to the FormSheet JS API and Fabric native component props.
  • Added onNativeDismissPrevented event plumbing (codegen prop → C++ event emitter → ObjC++ event emitter → JS).
  • Implemented iOS native prevention via UIAdaptivePresentationControllerDelegate and a custom backdrop UITapGestureRecognizer, plus added an SFT scenario for manual validation.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/fabric/gamma/modals/form-sheet/FormSheetHostNativeComponent.ts Adds codegen prop/event definitions for preventNativeDismiss + onNativeDismissPrevented.
src/components/gamma/modals/form-sheet/FormSheet.types.ts Exposes the new prop + event in the public TS types/docs.
ios/gamma/modals/form-sheet/RNSFormSheetHostEventEmitter.mm Emits onNativeDismissPrevented to JS when dismissal is blocked.
ios/gamma/modals/form-sheet/RNSFormSheetHostEventEmitter.h Declares emitOnNativeDismissPrevented.
ios/gamma/modals/form-sheet/RNSFormSheetHostComponentView.mm Tracks preventNativeDismiss prop and forwards it to the controller; emits prevented event via delegate callback.
ios/gamma/modals/form-sheet/RNSFormSheetContentController.mm Implements presentationControllerShouldDismiss / ...DidAttemptToDismiss and backdrop-tap interception via gesture recognizer.
ios/gamma/modals/form-sheet/RNSFormSheetContentController.h Adds delegate method + preventNativeDismiss property.
apps/src/tests/single-feature-tests/form-sheet/test-form-sheet-prevent-native-dismiss-ios/scenario.md Adds manual test scenario documentation for the new behavior.
apps/src/tests/single-feature-tests/form-sheet/test-form-sheet-prevent-native-dismiss-ios/index.tsx Adds an SFT screen to exercise the new prop/event.
apps/src/tests/single-feature-tests/form-sheet/index.ts Registers the new SFT scenario in the FormSheet group.

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

Comment thread ios/gamma/modals/form-sheet/RNSFormSheetHostComponentView.mm
Comment thread src/components/gamma/modals/form-sheet/FormSheet.types.ts Outdated
@t0maboro t0maboro force-pushed the @t0maboro/formsheet-modal-v5 branch from 9eebb45 to 7efa595 Compare May 14, 2026 09:39
Base automatically changed from @t0maboro/formsheet-modal-v5 to main May 14, 2026 10:30
@t0maboro t0maboro force-pushed the @t0maboro/form-sheet-prevent-native-dismiss branch from 098b6c8 to 322ae24 Compare May 14, 2026 10:58
t0maboro and others added 3 commits May 15, 2026 10:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@t0maboro t0maboro force-pushed the @t0maboro/form-sheet-prevent-native-dismiss branch from 322ae24 to 804c8c7 Compare May 15, 2026 08:14
Copy link
Copy Markdown
Contributor

@kligarski kligarski left a comment

Choose a reason for hiding this comment

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

Screen.Recording.2026-05-18.at.17.53.06.mov

might be related to missing detachBackdropTapGestureRecognizer call

Comment on lines +83 to +84
* The default value is `false`.
*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
* The default value is `false`.
*
*
* @default false

}
}

- (void)detachBackdropTapGestureRecognizer
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This isn't used right now - should it be?


## Note

- The default value of `preventNativeDismiss` is `false`.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm not sure why this information is placed in scenario. It's a bit misleading, taking into consideration the fact that this option is already enabled when we open the screen (which is the expected state). Could you explain why we need it here?

@@ -0,0 +1,81 @@
# Test Scenario: preventNativeDismiss
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could we capitalize the scenario name to PreventNativeDismiss so that it's consistent with both the other scenarios and its own scenarioDescription name?

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.

4 participants