Skip to content
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

Closed
thomasttvo opened this issue Oct 23, 2023 · 8 comments · Fixed by #2325 or react-navigation/react-navigation#12125
Closed
Assignees
Labels
Missing info The user didn't precise the problem enough Missing repro This issue need minimum repro scenario Platform: Android This issue is specific to Android Platform: iOS This issue is specific to iOS

Comments

@thomasttvo
Copy link

thomasttvo commented Oct 23, 2023

Description

on iOS:

  • When the title option is used on ScreenStackHeaderConfig, then the long title works great. image

  • However, when we use ScreenStackHeaderCenterView, the long title overlaps the right element. image

on Android:

  • The long title overlaps the right element in both cases

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

@github-actions github-actions bot added Missing repro This issue need minimum repro scenario Missing info The user didn't precise the problem enough labels Oct 23, 2023
@github-actions
Copy link

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?

@github-actions
Copy link

github-actions bot commented Oct 23, 2023

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.

@github-actions github-actions bot added Platform: Android This issue is specific to Android Platform: iOS This issue is specific to iOS labels Oct 23, 2023
@thomasttvo thomasttvo changed the title Long titles don't get truncated on native screens Long titles don't get truncated on native stack screens Oct 23, 2023
@tboba
Copy link
Member

tboba commented Oct 24, 2023

Hi @thomasttvo, thanks for submitting the issue!
Unfortunately this bug is not that easy to resolve - as for subviews of the header we're basically just using Gravity to position the elements, we're not calculating dynamically if the one subview will overlap another. Currently we've got planned something other to go around this bug, which is... to just toolbar.isTitleCentered for centerizing the title on Android (which already truncates the title) 😅
This should fully fix this bug, as i believe centerizing the title for iOS is not necessary, since iOS already have the title centered?

More or less, I think centerizing the header's title will be much better than using center view for rendering header's title.

@thomasttvo
Copy link
Author

thomasttvo commented Oct 24, 2023

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 title? That's great if it works. We can switch to just using title if we need to.

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.

@hugo-advizr
Copy link

we're also facing this issue, any updates on this? thanks

@kkafar
Copy link
Member

kkafar commented Jun 8, 2024

Yeah, I think I have some insight here now (on iOS).

Recently when working on UIBackButtonDisplayMode support we've realised that this is OS behaviour when using anything custom in navigation item it disables certain native behaviours. Dunno what to do in this case yet, but it came to my mind that it might be worth trying out the new prop ☝🏻

@ApplyFrank
Copy link

ApplyFrank commented Jul 8, 2024

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
react-navigation/react-navigation#12048

@kkafar
Copy link
Member

kkafar commented Jul 9, 2024

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 AppCompatTextView. This happens when the title is passed as raw string (no component) and no headerLeft child is passed to header, because then we just pass the text to the Android API (AppBar.title) & it handles it.

In case when it is not properly truncated, so when you specify headerLeft component or headerTitle is a component, not raw text, we:

  1. render both components as children of headerSubviewLeft,
  2. render title inside a react's text view.

What's important here: react's text view is laid out by Yoga. Yoga does use flexShrink: 0 by default (in opposition to web, where flexShrink: 1 is used) & we do not specify otherwise in HeaderTitle component. Basically we encounter the very problem described in this talk. If we set flexShrink: 1 in HeaderTitle component the text will be truncated correctly unless headerRight is specified. In such case, Yoga, for some yet unknown reason, does not respect headerRight component.

If you don't have headerRight you can workaround the issue by passing headerTitle as component and passing flexShrink: 1 to the text component (container) you're using.

Possible solutions

Undecided yet.

kkafar added a commit that referenced this issue Oct 8, 2024
## 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
ja1ns pushed a commit to WiseOwlTech/react-native-screens that referenced this issue Oct 9, 2024
## 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Missing info The user didn't precise the problem enough Missing repro This issue need minimum repro scenario Platform: Android This issue is specific to Android Platform: iOS This issue is specific to iOS
Projects
None yet
6 participants