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

fix(iOS): back button does not respect I18nManager & improve RTL handling in header #2185

Merged
merged 6 commits into from
Jun 27, 2024

Conversation

alduzy
Copy link
Member

@alduzy alduzy commented Jun 14, 2024

Description

This PR intents to fix back button direction in RTL mode forced by I18nManager for testing purposes on iOS.

When the application's language is changed using the CFBundleDevelopmentRegion the system is well aware of that, passes the information to React Native and Yoga. The result is a fully LTR / RTL compliant application.

However, when the RTL mode is set using the React Native's I18nManager.forceRTL() method (for testing purposes I believe) The change is applied to react views only and the system itself does not change it's direction.
In such case the only way for the native elements, such as navigationBar to adapt to the direction set by I18nManager is by manually applying the semanticContentAttribute based on direction prop passed by react-navigation.

Note

In case there is another native navigationBar element apart from the one coming from react-native-screens, this change may override it's direction.

Tested successfully on iOS 17.5 and 16.4

Fixes #1884 .

Changes

  • removed faulty workaround
  • set correct semanticContentAttribute for navigationBar and it's contents
  • refactored how the searchbar direction is set

Screenshots / GIFs

Before

image

After

image

Test code and steps to reproduce

  • play around with LTR and RTL mode in the example app

Checklist

Short story on how RTL management works in RN apps on iOS by @kkafar

Note

Basically we have two RTL management systems: native one & ReactNative's I18nManager.

Native one works most likely by applying semantic content attribute to all the views / classes and impacting layout constraints, thus whole native application is laid out properly.
We can impact RTL on natively managed views by:

Topic's to research:

  • how parent's semantic content attribute (set directly on given view) does affect subviews
  • exact semantics of using appearance proxy object, their priority and resolve order.

React Native has notion of internationalisation manager (I18n) which works as follows:

  • The RTL is allowed by default.
  • If RTL is not forced from JS, and allowed, then system language writing direction is resolved based on system calls and such obtained interface direction is passed down to Yoga impacting all views that are laid out by Yoga, but not purely native ones such as our header. Thus leading to conformity between system options and RN app options.
  • If RTL is forced from JS and allowed, them Yoga managed views are laid out in RTL mode, while system managed ones respect the system settings.
  • ForceRTL / AllowRTL flags are stored in NSUserDefaults application persistent storage, thus living through application restarts. These flags are RN specific, and system has no knowledge of them.

React Navigation takes the value from RN I18n system and passes it down to our screen / stack / header components.

@alduzy alduzy requested a review from WoLewicki June 17, 2024 09:41
Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A typo here, and I'll have some remarks to the PR description, so that we describe the whole mechanism in details, so that our debugging hours don't go to waste & we have good starting point for refactoring current header config code.

Regarding the sole logic changes in code - I'm happy with current solution 💪🏻

ios/RNSScreenStackHeaderConfig.mm Outdated Show resolved Hide resolved
@kkafar kkafar changed the title fix(iOS): backbutton RTL direction fix(iOS): back button does not respect I18nManager & improve RTL handling in header Jun 26, 2024
@alduzy alduzy requested a review from kkafar June 27, 2024 06:55
Copy link
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's proceed 💪🏻

@alduzy alduzy merged commit 114fbaa into main Jun 27, 2024
5 checks passed
@alduzy alduzy deleted the @alduzy/ios-backbutton-rtl branch June 27, 2024 07:53
alduzy added a commit that referenced this pull request Jun 28, 2024
…ling in header (#2185)

## Description

This PR intents to fix back button direction in RTL mode forced by
I18nManager for testing purposes on iOS.

When the application's language is changed using the
`CFBundleDevelopmentRegion` the system is well aware of that, passes the
information to React Native and Yoga. The result is a fully LTR / RTL
compliant application.

However, when the `RTL` mode is set using the React Native's
`I18nManager.forceRTL()` method (for testing purposes I believe) The
change is applied to react views only and the system itself does not
change it's direction.
In such case the only way for the native elements, such as
`navigationBar` to adapt to the direction set by `I18nManager` is by
manually applying the `semanticContentAttribute` based on `direction`
prop passed by `react-navigation`.

> [!note]
In case there is another native `navigationBar` element apart from the
one coming from `react-native-screens`, this change may override it's
direction.

Tested successfully on iOS 17.5 and 16.4

Fixes #1884 .

## Changes

- removed faulty workaround
- set correct semanticContentAttribute for navigationBar and it's
contents
- refactored how the searchbar direction is set

## Screenshots / GIFs

### Before

![image](https://github.com/software-mansion/react-native-screens/assets/91994767/1c67071d-92e4-4966-9ad8-362c54c4435b)

### After

![image](https://github.com/software-mansion/react-native-screens/assets/91994767/5cf40cf1-b762-4720-af33-0f939adac7c5)

## Test code and steps to reproduce

- play around with LTR and RTL mode in the example app

## Checklist

- [ ] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Updated documentation: <!-- For adding new props to native-stack
-->
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [ ] Ensured that CI passes

## Short story on how RTL management works in RN apps on iOS by @kkafar

> [!note]
> Basically we have two RTL management systems: native one &
ReactNative's I18nManager.
>
> Native one works most likely by applying semantic content attribute to
all the views / classes and impacting layout constraints, thus whole
native application is laid out properly.
> We can impact RTL on natively managed views by:
> * setting semantic content attributes for particular view
> * [setting semantic content attributes for particular class through
appearance
proxy](https://developer.apple.com/documentation/uikit/uiappearance?language=objc)
> * [setting semantic content attributes for particular class contained
in instances of another
class](https://developer.apple.com/documentation/uikit/uiappearance/1615006-appearancewhencontainedin?language=objc)
> 
> Topic's to research:
> * how parent's semantic content attribute (set directly on given view)
does affect subviews
> * exact semantics of using appearance proxy object, their priority and
resolve order.
> 
> 
> React Native has notion of internationalisation manager (I18n) which
works as follows:
> 
> * The RTL is allowed by default.
> * If RTL is not forced from JS, and allowed, then system language
writing direction is resolved based on system calls and such obtained
interface direction is passed down to Yoga *impacting all views that are
laid out by Yoga*, but not purely native ones such as our header. Thus
leading to conformity between system options and RN app options.
> * If RTL is forced from JS and allowed, them Yoga managed views are
laid out in RTL mode, while system managed ones respect the system
settings.
> * ForceRTL / AllowRTL flags are stored in `NSUserDefaults` application
persistent storage, thus living through application restarts. These
flags are RN specific, and system has no knowledge of them.
> 
> React Navigation takes the value from RN I18n system and passes it
down to our screen / stack / header components.

---------

Co-authored-by: Kacper Kafara <[email protected]>
Co-authored-by: Kacper Kafara <[email protected]>
ja1ns pushed a commit to WiseOwlTech/react-native-screens that referenced this pull request Oct 9, 2024
…ling in header (software-mansion#2185)

## Description

This PR intents to fix back button direction in RTL mode forced by
I18nManager for testing purposes on iOS.

When the application's language is changed using the
`CFBundleDevelopmentRegion` the system is well aware of that, passes the
information to React Native and Yoga. The result is a fully LTR / RTL
compliant application.

However, when the `RTL` mode is set using the React Native's
`I18nManager.forceRTL()` method (for testing purposes I believe) The
change is applied to react views only and the system itself does not
change it's direction.
In such case the only way for the native elements, such as
`navigationBar` to adapt to the direction set by `I18nManager` is by
manually applying the `semanticContentAttribute` based on `direction`
prop passed by `react-navigation`.

> [!note]
In case there is another native `navigationBar` element apart from the
one coming from `react-native-screens`, this change may override it's
direction.

Tested successfully on iOS 17.5 and 16.4

Fixes software-mansion#1884 .

## Changes

- removed faulty workaround
- set correct semanticContentAttribute for navigationBar and it's
contents
- refactored how the searchbar direction is set

## Screenshots / GIFs

### Before

![image](https://github.com/software-mansion/react-native-screens/assets/91994767/1c67071d-92e4-4966-9ad8-362c54c4435b)

### After

![image](https://github.com/software-mansion/react-native-screens/assets/91994767/5cf40cf1-b762-4720-af33-0f939adac7c5)

## Test code and steps to reproduce

- play around with LTR and RTL mode in the example app

## Checklist

- [ ] Included code example that can be used to test this change
- [ ] Updated TS types
- [ ] Updated documentation: <!-- For adding new props to native-stack
-->
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx
- [ ]
https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx
- [ ] Ensured that CI passes

## Short story on how RTL management works in RN apps on iOS by @kkafar

> [!note]
> Basically we have two RTL management systems: native one &
ReactNative's I18nManager.
>
> Native one works most likely by applying semantic content attribute to
all the views / classes and impacting layout constraints, thus whole
native application is laid out properly.
> We can impact RTL on natively managed views by:
> * setting semantic content attributes for particular view
> * [setting semantic content attributes for particular class through
appearance
proxy](https://developer.apple.com/documentation/uikit/uiappearance?language=objc)
> * [setting semantic content attributes for particular class contained
in instances of another
class](https://developer.apple.com/documentation/uikit/uiappearance/1615006-appearancewhencontainedin?language=objc)
> 
> Topic's to research:
> * how parent's semantic content attribute (set directly on given view)
does affect subviews
> * exact semantics of using appearance proxy object, their priority and
resolve order.
> 
> 
> React Native has notion of internationalisation manager (I18n) which
works as follows:
> 
> * The RTL is allowed by default.
> * If RTL is not forced from JS, and allowed, then system language
writing direction is resolved based on system calls and such obtained
interface direction is passed down to Yoga *impacting all views that are
laid out by Yoga*, but not purely native ones such as our header. Thus
leading to conformity between system options and RN app options.
> * If RTL is forced from JS and allowed, them Yoga managed views are
laid out in RTL mode, while system managed ones respect the system
settings.
> * ForceRTL / AllowRTL flags are stored in `NSUserDefaults` application
persistent storage, thus living through application restarts. These
flags are RN specific, and system has no knowledge of them.
> 
> React Navigation takes the value from RN I18n system and passes it
down to our screen / stack / header components.

---------

Co-authored-by: Kacper Kafara <[email protected]>
Co-authored-by: Kacper Kafara <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

headerLargeTitle & headerSearchBarOptions Doesn't Support RTL
3 participants