Skip to content

Conversation

@kligarski
Copy link
Contributor

@kligarski kligarski commented Mar 20, 2025

Description

Adds new presentation for Native Stack - pageSheet (UIModalPresentationPageSheet modal style on iOS, modal on Android).

Since the release of iOS 18, screens with stackPresentation: 'modal' changed size on devices with a bigger screen, such as an iPad - they became much smaller, which impacted applications of the library's users (see issue #2549). This happens because UIModalPresentationAutomatic, which is used by react-native-screens for modal on iOS, has been changed with the release of iOS 18. UIModalPresentationAutomatic is now mapped to UIModalPresentationFormSheet for iOS >=18 and UIModalPresentationPageSheet for earlier versions (this was the default before).

Apple added SwiftUI API (link) to allow to choose the behavior of the modal (form, page, fitted or a custom one) but I wasn't able to find an equivalent functionality in UIKit. It turns out there is a new property in UISheetPresentationController, see here

We considered 3 possible solutions to this problem:

  • changing modal presentation style on iOS from UIModalPresentationAutomatic to UIModalPresentationPageSheet for stackPresentation: 'modal' to bring back old behavior for all modals:
    • some users might've already designed their apps with the smaller sheet in mind so changing the style might break their apps,
  • adding a feature flag that would allow users to choose whether screens with stackPresentation: 'modal' should use UIModalPresentationAutomatic or UIModalPresentationPageSheet
    • this solution would provide the easiest fix for the users impacted by the change,
    • even though 2 different sheet sizes would be available, users would need to choose only one of them for their application,
  • adding a new stackPresentation that would use UIModalPresentationPageSheet on iOS and a regular modal on Android:
    • users can choose whether they want to use Automatic or PageSheet behavior per-screen basis,
    • hopefully, fixing old applications with some find-and-replace magic won't be difficult.

We decided on the third option.

tvOS does not allow UIModalPresentationPageSheet so it fallbacks to UIModalPresentationFullScreen (the same behavior as UIModalPresentationAutomatic).

This change will require changes to react-navigation/native-stack as well.

Resolves #2549.

Changes

  • add support for the new presentation in native and JS code
  • add pageSheet examples in Stack Presentation and Modals example screens

Screenshots / GIFs

modal

modal

pageSheet

pageSheet

Test code and steps to reproduce

Open Stack Presentation, Modals example screens and open pageSheet (at the moment, you might need to manually add pageSheet in react-navigation files packages/native-stack/src, packages/native-stack/src/utils/getModalRoutesKeys.ts, packages/native-stack/src/views).

Checklist

Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

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

I have found that this API got exposed by Apple:

https://developer.apple.com/documentation/uikit/uisheetpresentationcontroller/preferspagesizing?language=objc

& it is equivalent of the one in SwiftUI https://developer.apple.com/documentation/swiftui/presentationsizing

From the docs:

Discussion
The default value is true. The default value indicates the sheet uses UIModalPresentationPageSheet behavior, in which the sheet width follows the readable width.

When the value is set to false, the sheet uses UIModalPresentationFormSheet behavior, in which the sheet size follows the presented view controller’s preferredContentSize.

So I guess we have a couple of ways of solving this now.

1. Expose global switch in JS

We could expose global switch in JS to let users decide on the desired behaviour.

E.g. in utils.ts, where we have compatibilityFlags right now, we could add add a new global object & let users mutate it.

We would then read the value & pass it down via extra prop on screen & select appropriate behaviour.

2. Expose the switch on Screen definition level

Basically the same idea as above, but the prop can be passed to each Screen directly, allowing for a bit more of flexibility.

3. Treat is as the fix & restore the old behaviour

We could use the new API to restore the old behaviour w/o exposing any customisation option to the user.

4. Mix of previous 3

☝🏻

Summary

The change introduced in iOS between 17 & 18 is treated as breaking by the users, and indeed can be seen as one.

Therefore first thing to do, would be to restore the previous behaviour. We want to go back to using UIModalPresentationPageSheet on iOS 18 and above.
To achieve this we could e.g. use the new API & update the modal behaviour documentation, to indicate that it does enforce pageSheet on iOS >= 18. Later, when releasing Screens 5.0 we could restore the modal to just use UIModalPresentationAutomatic, but for now we want to mitigate this breaking change.

Second thing to do is to expose new system behaviour somehow. We expose formSheet presentation type & it uses UIModalPresentationFormSheet underneath, therefore can be used to achieve this, however due to #2045 and series of later changes it is treated a bit specially now (has layout limitations etc.), and due to that comes with series of pain points.

Therefore the action points:

  1. Restore pagesheet behaviour in regular size classes (e.g. by using the new API)
  2. update the modal documentation, to indicate that it not only uses UIModalPresentationAutomatic but also the prefersPageSizing (effectively using UIModalPresentationPageSheet)
  3. We'll expose the pageSheet option later, after we fix above ☝🏻 (this will also be released on v4 line)
  4. We'll restore the modal presentation behaviour to use UIModalPresentationAutomatic purely in [email protected] (next major)

@kligarski
Copy link
Contributor Author

Okay. The change for v4 is here: #2797.

kligarski added a commit that referenced this pull request Mar 24, 2025
## Description

Brings back `stackPresentation: 'modal'`'s size on iOS 18.0+.

Since the release of iOS 18, screens with `stackPresentation: 'modal'`
changed size on devices with a bigger screen, such as an iPad - they
became much smaller, which impacted applications of the library's users
(see issue
#2549).
This happens because `UIModalPresentationAutomatic`, which is used by
`react-native-screens` for modal on iOS, has been changed with the
release of iOS 18. `UIModalPresentationAutomatic` is now mapped to
`UIModalPresentationFormSheet` for iOS >=18 and
`UIModalPresentationPageSheet` for earlier versions (this was the
default before).

For more context please see PR
#2793 but
the TL;DR is that we consider this a breaking change for our users and
therefore we decided to bring back the old behavior for
`react-native-screens` v4 using
[`prefersPageSizing`](https://developer.apple.com/documentation/uikit/uisheetpresentationcontroller/preferspagesizing?language=objc)
property of the `UISheetPresentationController` which has been available
since iOS 17.0+.

Resolves
#2549.

## Changes

- use `prefersPageSizing` for modal on iOS 18.0+

## Screenshots / GIFs

### Before
![Modal without `prefersPageSizing` on iOS
18](https://github.com/user-attachments/assets/724bd109-b0c0-47e4-b897-43cd53ef3dd9)

### After
![Modal with `prefersPageSizing` on iOS
18](https://github.com/user-attachments/assets/b0925934-25dc-4103-8422-8fb07a18fd18)

## Test code and steps to reproduce

Open `Modals` example screen and use `Open modal` button.

## Checklist

- [x] Included code example that can be used to test this change
- [x] Ensured that CI passes

---------

Co-authored-by: Kacper Kafara <[email protected]>
@kligarski kligarski requested a review from kkafar March 25, 2025 14:47
@kkafar kkafar marked this pull request as ready for review March 28, 2025 10:09
Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

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

This looks solid, good job 💪🏻

I have a question 👇🏻 and I haven't tested the runtime yet.

@kkafar
Copy link
Member

kkafar commented Mar 28, 2025

Also do we have a PR prepared for react-navigation to expose this new modal type? Are there any changes needed there?

@kligarski
Copy link
Contributor Author

PR for react-navigation: react-navigation/react-navigation#12503

Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

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

Answered

Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

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

I think we're good. Let's proceed.

@kkafar kkafar changed the title feat: add pageSheet presentation for native-stack feat(iOS): add pageSheet presentation for native-stack Mar 28, 2025
@kligarski kligarski merged commit 4d044da into main Mar 28, 2025
11 checks passed
@kligarski kligarski deleted the @kligarski/add-page-sheet-presentation branch March 28, 2025 11:30
kligarski added a commit to react-navigation/react-navigation that referenced this pull request Nov 24, 2025
**Motivation**

Since the release of iOS 18, screens with`presentation: 'modal'` changed
size on devices with a bigger screen, such as an iPad - they became much
smaller, which impacted applications of the library's users (see issue
software-mansion/react-native-screens#2549).

To address the issue, `react-native-screens` will introduce new
presentation type for native stack (`pageSheet`) that will allow users
to use previous modal behavior (see PR
software-mansion/react-native-screens#2793). The
behavior of `pageSheet` on Android will be the same as `modal`.

Please note that in `react-native-screens`, behavior of regular `modal`
has been aligned with previous behavior internally via
[`prefersPageSizing` UIKit
prop](software-mansion/react-native-screens#2797)
in order not to introduce breaking changes. In the future major release
of `react-native-screens` however this will be aligned to fully native
behavior.

This PR contains changes required to support `pageSheet` in
`react-navigation`.

**Test plan**

Open Stack Presentation and Modals example screens in example apps from
`react-native-screens` and open a `pageSheet`.
satya164 pushed a commit to react-navigation/react-navigation that referenced this pull request Nov 24, 2025
**Motivation**

Since the release of iOS 18, screens with`presentation: 'modal'` changed
size on devices with a bigger screen, such as an iPad - they became much
smaller, which impacted applications of the library's users (see issue
software-mansion/react-native-screens#2549).

To address the issue, `react-native-screens` will introduce new
presentation type for native stack (`pageSheet`) that will allow users
to use previous modal behavior (see PR
software-mansion/react-native-screens#2793). The
behavior of `pageSheet` on Android will be the same as `modal`.

Please note that in `react-native-screens`, behavior of regular `modal`
has been aligned with previous behavior internally via
[`prefersPageSizing` UIKit
prop](software-mansion/react-native-screens#2797)
in order not to introduce breaking changes. In the future major release
of `react-native-screens` however this will be aligned to fully native
behavior.

This PR contains changes required to support `pageSheet` in
`react-navigation`.

**Test plan**

Open Stack Presentation and Modals example screens in example apps from
`react-native-screens` and open a `pageSheet`.
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.

presentation: 'modal' Screens are tiny on iPad / iOS18

3 participants