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