feat(iOS, Stack): New implementation for calculating edgeInsets
Description
Closes: https://github.com/software-mansion/react-native-screens-labs/issues/352
Should be reviewed/merged after https://github.com/software-mansion/react-native-screens/pull/2987 - as for now, moving to draft.
Problem definition
On iOS 26, when headerTitle is a component containing text that overflows its parent container, the text is not correctly truncated. Instead, part of it is displayed underneath the right header button.
Root cause analysis
After the changes introduced in iOS 26, our current method of calculating edgeInsets, which are based on the sum of the UINavigationBar.directionalLayoutMargins and its content directionalLayoutMargins is insufficient. The problem also lies in the fact that by calculating insets only based on these components, we lack full information about the positions of the buttons within the navigation bar. These insets are approximate, which may lead to a gap between the left/right buttons and the title component.
Solution
With a flexbox-based model, a better approach would be to imagine that all React components are wrapped in a single component in which they're layouting in a flex manner. This way, the layout of all HeaderSubviews can be fully delegated to Yoga. This is achievable by reading the left edge of the ScreenStackHeaderLeftView and the right edge of the ScreenStackHeaderRightView. In the current model, this allows Yoga to operate in the area:
<ScreenStackHeaderConfig >
<ScreenStackHeaderLeftView />
<ScreenStackHeaderCenterView />
<ScreenStackHeaderRightView />
<ScreenStackHeaderConfig />
This enables the text to stretch across the full available width, either if flex: 1 is provided or if the text is long enough to require truncating.
Known issues
-
This approach for calculating insets requires a fully computed layout in the
UINavigationBar. Therefore, during transitions, a correct update may not occur. Detection of the proper moment when update should come is problematic, what is resulting in content jumping during forward navigation. However, once the transition completes, the header stabilizes. This issue only affects headers where components do not have a fixed width. -
A new feature — support for
UIBarButtonItemis causing layout issues because if a button is native, from Yoga’s perspective, it should be ignored for layout. For example, if there is one native button on both the left and right sides, we should take insets based on the right edge ofheaderLeftItemsand the left edge ofheaderRightItems. Additionally, we need to clearly define whether we allow mixing of React components and native components inheader[Left/Right]Itemsprops.
Testing coverage
- [x] All TCs from the sheet
- [x] BackTitle and BackButton support
- [x] RTL support
- [ ] Multiple left & right (
UIBarButtonItem) buttons support
Changes
- added a new algorithm for calculating the space for React components in header
- hidden new implementation only to work with iOS 26+ to prevent any regression with older version
- added
overflow: hiddento mitigate jumping content on thepushtransition issue
Screenshots / GIFs
Before
https://github.com/user-attachments/assets/6e2e7a82-0fca-42bf-9e17-ab557dc9219a
After
https://github.com/user-attachments/assets/87e9c073-2f9f-4b89-8b8b-fd0de71eb474
Test code and steps to reproduce
I believe that this may require more integration testing with other APIs in the future; therefore, I added a new example that will collect all new TC we should cover (e.g., support for UIBarButtonItems soon).
Checklist
- [x] Included code example that can be used to test this change
- [x] Ensured that CI passes
Further steps
For this PR, I need to create follow-up tickets for taking further actions:
- https://github.com/software-mansion/react-native-screens-labs/issues/429 - truncating text results in the content flicker at the moment, we should put it under further investigation to determine whether we can resolve this issue and unblock calculating layout during the transition
- https://github.com/software-mansion/react-native-screens-labs/issues/430 -
headerTitleprop with RN component passed in it is causing several issues in the native-stack implementation. This ticket is to determine whether we should focus on resolving them or rather add support for all native title customizations, which would guarantee "native feeling"