-
-
Notifications
You must be signed in to change notification settings - Fork 530
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Long titles don't get truncated on native stack screens #1946
Long titles don't get truncated on native stack screens #1946
Comments
Hey! 👋 The issue doesn't seem to contain a minimal reproduction. Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem? |
Hey! 👋 It looks like you've omitted a few important sections from the issue template. Please complete Snack or a link to a repository section. |
Hi @thomasttvo, thanks for submitting the issue! More or less, I think centerizing the header's title will be much better than using center view for rendering header's title. |
do you mean that will fix Android in the case of using Would still be great though for it to work with custom title views. Possibly wrap the subview in another view that has some dynamic measurement might be the solution. |
we're also facing this issue, any updates on this? thanks |
Yeah, I think I have some insight here now (on iOS). Recently when working on |
still happening, I filed a bug in React Navigation, linking here since their contributor suggested I post in react-native-screens since it uses react-native-screens |
Hey, I will update you here a bit, as we did spend some time debugging it. @alduzy please correct my message if I'm wrong at some point. Talking Android here: The text is properly truncated when it is mounted natively as In case when it is not properly truncated, so when you specify
What's important here: react's text view is laid out by Yoga. Yoga does use If you don't have Possible solutionsUndecided yet. |
## Description > [!caution] This PR includes **BREAKING CHANGES** Corresponding PR in `react-navigation`: * react-navigation/react-navigation#12125 Fixes #1946 This PR refactors the header config component to use flex-box model instead of absolute positioning in Yoga layer. This is required so that the Yoga layouts children of header config with respect to one another (not absolutely), so that the title can be properly truncated. In the end, the subviews are laid out by system anyway, thus we send additional information, such as padding / margins from native side to the shadow tree, so that we can adjust for these in Yoga layout. > [!important] This PR introduces a bug in very first few frames - before the information from HostTree is propagated to ShadowTree the header title might not be truncated properly. This is known issue and based on the same mechanism and "jumping content" due to not including header dimensions in first Yoga layout. <!-- ### Before / After `headerTitleAlign: 'left'`, long title as string ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/ec7dd405-7244-4c18-b96d-5ffd79be78b8"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/7b0086b5-3554-4b76-99d5-302d03cab96e"></img> | | Works fine, cause `title` is passed as regular string to native side, where it is simply assigned to `toolbar.title` & `AppCompatTextView` is used by Android. `headerTitleAlign: 'left'`, `headerLeft`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/228f64b9-4ca8-4c65-8128-a5097bbae595"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/e1426702-a020-4113-a271-588c62e385db"></img> | | Title despite being a plain string is wrapped inside `HeaderTitle` component & rendered as a `Text` inside `SubviewLeft` together with `HeaderLeft`. The text overflows, because subview is positioned as `absolute`: it does not respect native toolbar's content inset (left padding), as Yoga has no information of it & the text view does measure itself with width of whole containing box (which is the `HeaderConfig` which has width of full screen). What's also important to notice is that in this configuration `backButtonIcon` is disabled. `headerTitleAlign: 'left'`, `headerRight`, long title as string ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/2af7a845-4c7c-419a-a9bc-5e44525fb935"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/7afaa643-7a00-42a4-8cb2-3d4fa9a9781b"></img> | | Works ok, because text is assigned to `toolbar.title`, thus `AppCompatTextView` is in use & native layout takes care of truncating the text & respecting the `HeaderRight`. We good here. `headerTitleAlign: 'left'`, `headerLeft`, `headerRight`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/186b8534-00e0-4dc6-b4b0-45a12e9a84c5"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/3bfc1bd6-3e9f-4ede-a375-7fa329e6a4f0"></img> | | Text is put together with `HeaderLeft` into `SubviewLeft` & rendered inside `Text`. Subviews are positioned as `absolute` & Yoga measures text with full width of the screen & does not take into account `absolute` siblings. Notice that in the second row of screenshots `backButtonIcon` is also not present. `headerTitleAlign: 'left'`, long title as `Text` ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/ec7dd405-7244-4c18-b96d-5ffd79be78b8"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/7b0086b5-3554-4b76-99d5-302d03cab96e"></img> | | `headerTitleAlign: 'left'`, `headerLeft`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/34046637-886e-44e7-8588-2143cc5ec7bc"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/b57d9beb-0c5e-405d-b920-393bee801a07"></img> | | `headerTitleAlign: 'left'`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/83afc32a-b8f5-4ffc-9570-8b9034e4abb9"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/04d58470-53c3-45bb-a335-f6265b34c1e6"></img> | | `headerTitleAlign: 'left'`, `headerLeft`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/30dcc02e-4fa4-4a89-9add-b925e1290bb2"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/716de207-aeec-4766-8cab-09f535f2fac2"></img> | | `headerTitleAlign: 'center'`, long title as string ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/58194a20-9373-4352-a2b6-ada2982c2646"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/6c31121d-09d9-40f5-8bf8-688cd4d1a28e"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/c8c2507a-fb23-4265-b257-e2a469e0cbae"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/cdc6efaa-83f8-4e7d-9aba-68ad0b276bbb"></img> | | `headerTitleAlign: 'center'`, `headerRight`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/c83cf46a-0b5a-49cf-96b2-c602df090a83"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/e33623ae-0a32-46d6-9355-2ce353c60a66"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, `headerRight`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/a8c626b8-7b9d-4b91-b2f9-4a94ae10fd4a"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/90dc5994-2aca-4ce2-8360-4baea67734be"></img> | | `headerTitleAlign: 'center'`, long title as `Text` ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/b119f64a-4638-4e28-abf8-33b23290d195"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/0f0a92e4-305e-429a-b852-623dc06f43f8"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/1966cca9-1cce-47f7-8a87-a0b687edfe7a"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/5ea1bb3a-1926-4abf-839c-3ddac73c74aa"></img> | | `headerTitleAlign: 'center'`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/c2801a4d-7f63-4e67-acb3-0ce55e09e937"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/789de0b6-f703-45dd-aeb0-8e3082bbf267"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/9159990b-845f-4d26-b103-d1a5ecdec656"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/07cdf821-9503-4f68-a87f-9dc7a96ea032"></img> | | --> ## Changes <!-- Please describe things you've changed here, make a **high level** overview, if change is simple you can omit this section. For example: - Updated `about.md` docs --> <!-- ## Screenshots / GIFs Here you can add screenshots / GIFs documenting your change. You can add before / after section if you're changing some behavior. ### Before ### After --> ## Test code and steps to reproduce I've tried to test these changes carefully, however there still might be some bugs. It would be nice if we could get rid of them during beta-phase of 4.0. Below I paste my test matrix I conducted on `Test1649` on both platforms and architectures. ![image](https://github.com/user-attachments/assets/504fad12-dbd4-4648-871d-3c65215758ce) ## Checklist - [x] Included code example that can be used to test this change - [ ] Ensured that CI passes
## Description > [!caution] This PR includes **BREAKING CHANGES** Corresponding PR in `react-navigation`: * react-navigation/react-navigation#12125 Fixes software-mansion#1946 This PR refactors the header config component to use flex-box model instead of absolute positioning in Yoga layer. This is required so that the Yoga layouts children of header config with respect to one another (not absolutely), so that the title can be properly truncated. In the end, the subviews are laid out by system anyway, thus we send additional information, such as padding / margins from native side to the shadow tree, so that we can adjust for these in Yoga layout. > [!important] This PR introduces a bug in very first few frames - before the information from HostTree is propagated to ShadowTree the header title might not be truncated properly. This is known issue and based on the same mechanism and "jumping content" due to not including header dimensions in first Yoga layout. <!-- ### Before / After `headerTitleAlign: 'left'`, long title as string ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/ec7dd405-7244-4c18-b96d-5ffd79be78b8"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/7b0086b5-3554-4b76-99d5-302d03cab96e"></img> | | Works fine, cause `title` is passed as regular string to native side, where it is simply assigned to `toolbar.title` & `AppCompatTextView` is used by Android. `headerTitleAlign: 'left'`, `headerLeft`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/228f64b9-4ca8-4c65-8128-a5097bbae595"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/e1426702-a020-4113-a271-588c62e385db"></img> | | Title despite being a plain string is wrapped inside `HeaderTitle` component & rendered as a `Text` inside `SubviewLeft` together with `HeaderLeft`. The text overflows, because subview is positioned as `absolute`: it does not respect native toolbar's content inset (left padding), as Yoga has no information of it & the text view does measure itself with width of whole containing box (which is the `HeaderConfig` which has width of full screen). What's also important to notice is that in this configuration `backButtonIcon` is disabled. `headerTitleAlign: 'left'`, `headerRight`, long title as string ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/2af7a845-4c7c-419a-a9bc-5e44525fb935"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/7afaa643-7a00-42a4-8cb2-3d4fa9a9781b"></img> | | Works ok, because text is assigned to `toolbar.title`, thus `AppCompatTextView` is in use & native layout takes care of truncating the text & respecting the `HeaderRight`. We good here. `headerTitleAlign: 'left'`, `headerLeft`, `headerRight`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/186b8534-00e0-4dc6-b4b0-45a12e9a84c5"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/3bfc1bd6-3e9f-4ede-a375-7fa329e6a4f0"></img> | | Text is put together with `HeaderLeft` into `SubviewLeft` & rendered inside `Text`. Subviews are positioned as `absolute` & Yoga measures text with full width of the screen & does not take into account `absolute` siblings. Notice that in the second row of screenshots `backButtonIcon` is also not present. `headerTitleAlign: 'left'`, long title as `Text` ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/ec7dd405-7244-4c18-b96d-5ffd79be78b8"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/7b0086b5-3554-4b76-99d5-302d03cab96e"></img> | | `headerTitleAlign: 'left'`, `headerLeft`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/34046637-886e-44e7-8588-2143cc5ec7bc"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/b57d9beb-0c5e-405d-b920-393bee801a07"></img> | | `headerTitleAlign: 'left'`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/83afc32a-b8f5-4ffc-9570-8b9034e4abb9"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/04d58470-53c3-45bb-a335-f6265b34c1e6"></img> | | `headerTitleAlign: 'left'`, `headerLeft`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/30dcc02e-4fa4-4a89-9add-b925e1290bb2"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/716de207-aeec-4766-8cab-09f535f2fac2"></img> | | `headerTitleAlign: 'center'`, long title as string ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/58194a20-9373-4352-a2b6-ada2982c2646"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/6c31121d-09d9-40f5-8bf8-688cd4d1a28e"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/c8c2507a-fb23-4265-b257-e2a469e0cbae"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/cdc6efaa-83f8-4e7d-9aba-68ad0b276bbb"></img> | | `headerTitleAlign: 'center'`, `headerRight`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/c83cf46a-0b5a-49cf-96b2-c602df090a83"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/e33623ae-0a32-46d6-9355-2ce353c60a66"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, `headerRight`, long title as string ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/a8c626b8-7b9d-4b91-b2f9-4a94ae10fd4a"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/90dc5994-2aca-4ce2-8360-4baea67734be"></img> | | `headerTitleAlign: 'center'`, long title as `Text` ⬇️ | Before ✅ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/b119f64a-4638-4e28-abf8-33b23290d195"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/0f0a92e4-305e-429a-b852-623dc06f43f8"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/1966cca9-1cce-47f7-8a87-a0b687edfe7a"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/5ea1bb3a-1926-4abf-839c-3ddac73c74aa"></img> | | `headerTitleAlign: 'center'`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/c2801a4d-7f63-4e67-acb3-0ce55e09e937"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/789de0b6-f703-45dd-aeb0-8e3082bbf267"></img> | | `headerTitleAlign: 'center'`, `headerLeft`, `headerRight`, long title as `Text` ⬇️ | Before ❌ | After ❓| |--------|--------| | <img width="480" height="200" src="https://github.com/user-attachments/assets/9159990b-845f-4d26-b103-d1a5ecdec656"></img> | | | <img width="480" height="200" src="https://github.com/user-attachments/assets/07cdf821-9503-4f68-a87f-9dc7a96ea032"></img> | | --> ## Changes <!-- Please describe things you've changed here, make a **high level** overview, if change is simple you can omit this section. For example: - Updated `about.md` docs --> <!-- ## Screenshots / GIFs Here you can add screenshots / GIFs documenting your change. You can add before / after section if you're changing some behavior. ### Before ### After --> ## Test code and steps to reproduce I've tried to test these changes carefully, however there still might be some bugs. It would be nice if we could get rid of them during beta-phase of 4.0. Below I paste my test matrix I conducted on `Test1649` on both platforms and architectures. ![image](https://github.com/user-attachments/assets/504fad12-dbd4-4648-871d-3c65215758ce) ## Checklist - [x] Included code example that can be used to test this change - [ ] Ensured that CI passes
Description
on iOS:
When the
title
option is used onScreenStackHeaderConfig
, then the long title works great.However, when we use
ScreenStackHeaderCenterView
, the long title overlaps the right element.on Android:
Steps to reproduce
mentioned in description
Snack or a link to a repository
--
Screens version
3.25.0
React Native version
0.72.5
Platforms
Android, iOS
JavaScript runtime
None
Workflow
None
Architecture
None
Build type
None
Device
None
Device model
No response
Acknowledgements
Yes
The text was updated successfully, but these errors were encountered: