SIGN IN SIGN UP

feat(iOS, FormSheet v5): Introduce PresentationManager for FormSheet component (#4086)

## Description

This PR addresses cases where rapid updates to the `isOpen` prop from JS
may cause the native presentation state to desynchronize. Previously,
the presentation logic was primitive, and triggering a present or
dismiss while another UIKit animation was already in flight would cause
UIKit to ignore the call, permanently breaking the component's state.

I'm solving this by introducing a dedicated PresentationManager with a
state machine.

1. The PresentationManager will only fire a present if the current state
is `Dismissed`, and will only fire a dismiss if the state is
`Presented`.
2. If React toggles the `isOpen` prop while an animation (state
`Presenting` or `Dismissing`) is actively running, the manager
intentionally suppresses the call to prevent conflicts.
3. Inside the completion block of every UIKit animation, the manager
updates its internal state (from `Dismissing` to `Dismissed`, from
`Presenting` to `Presented`) and then immediately re-evaluates the
desired React state. If React updates the value mid-animation, the
manager instantly catches up and triggers the next transition.

Note:
The quick `isOpen` toggle may try to apply the configuration to an old
`sheetPresentationController` that we're going to destroy in `Dismissing
-> Dismissed` transition. I need to force a complete configuration
refresh inside `prepareForPresentation` to guarantee the newly recreated
UIKit controller always receives the latest props before it appears on
screen.

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

## Changes

- `RNSFormSheetPresentationState` was defined
- `RNSFormSheetPresentationManager` was defined to define the state
machine
- Stripped the presentation logic from `RNSFormSheetContentController`

## Before & after - visual documentation

| Before | After |
| --- | --- |
| <video
src="https://github.com/user-attachments/assets/e7901080-4cbe-46f0-866f-64ba88b52d79"
/> | <video
src="https://github.com/user-attachments/assets/7f9f6075-2015-4b38-98fc-c9865e8b6a79"
/> |

## Test plan

Added a new dedicated SFT that will toggle `isOpen` prop twice with
timeout 32ms.

## Checklist

- [ ] Included code example that can be used to test this change.
- [ ] For visual changes, included screenshots / GIFs / recordings
documenting the change.
- [ ] For API changes, updated relevant public types.
- [ ] Ensured that CI passes

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
T
Tomasz Boroń committed
223eb4256b2cff2f07c77243aa16f3a2d4d03ea2
Parent: 4a48ee4
Committed by GitHub <noreply@github.com> on 5/27/2026, 7:28:51 AM