Skip to content

Commit 1b92b02

Browse files
zoontekmeta-codesync[bot]
authored andcommitted
fix(ios): align text to the right by default in RTL (#57634)
Summary: On the new architecture, `<Text>` without an explicit `textAlign` behaves differently across platforms when the layout direction is RTL: - **Android**: text aligns to the right (follows the layout direction). - **iOS**: text stays left-aligned (ignores the layout direction). This is a regression from the old architecture, where both platforms left text alone when `textAlign` was unset. The new arch changed Android's default, and the two platforms are now inconsistent. This PR makes iOS match the current Android (new arch) behavior: without an explicit `textAlign`, text follows the layout direction and aligns to the right in RTL. On iOS, `Natural` alignment now resolves to `Right` under RTL. ### Open question I aligned iOS to Android's new default, but I'm not sure it's the right call (my guts feeling think that is the right thing to do, tho). The old-arch behavior (do nothing when `textAlign` is unset) may be what's actually expected, in which case the fix should go the other way: revert Android to leave text untouched and keep iOS as-is. Happy to flip the direction if reviewers prefer that. ## Changelog: [IOS] [FIXED] - Align text to the right by default in RTL when `textAlign` is not set, matching Android Pull Request resolved: #57634 Test Plan: Edit `RNTesterAppShared.js`, press the button, reload the app - comment / uncomment `Text` `textAlign` style: ```js /** * flow * format */ import { I18nManager, SafeAreaView, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; I18nManager.allowRTL(true); I18nManager.swapLeftAndRightInRTL(true); export default function App(): React.Node { const isRTL = I18nManager.isRTL; return ( <View style={styles.wrapper}> <SafeAreaView style={styles.content}> <Text style={styles.info}>I18nManager.isRTL: {String(isRTL)}</Text> <TouchableOpacity onPress={() => I18nManager.forceRTL(!isRTL)} style={styles.button}> <Text style={styles.buttonText}>Toggle RTL + reload manually</Text> </TouchableOpacity> <View style={styles.row}> <View style={[styles.box, {backgroundColor: '#FF6B6B'}]}> <Text style={styles.boxText}>1</Text> </View> <View style={[styles.box, {backgroundColor: '#4ECDC4'}]}> <Text style={styles.boxText}>2</Text> </View> <View style={[styles.box, {backgroundColor: '#45B7D1'}]}> <Text style={styles.boxText}>3</Text> </View> </View> <Text // style={{textAlign: 'left'}} // COMMENT / UNCOMMENT THIS > Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </Text> </SafeAreaView> </View> ); } const styles = StyleSheet.create({ wrapper: { backgroundColor: 'white', flex: 1, }, content: { gap: 20, marginHorizontal: 20, }, info: { fontSize: 18, fontWeight: 'bold', }, button: { backgroundColor: '#007AFF', padding: 15, borderRadius: 8, }, buttonText: { color: 'white', textAlign: 'center', fontWeight: '600', }, row: { flexDirection: 'row', justifyContent: 'space-around', }, box: { width: 60, height: 60, alignItems: 'center', justifyContent: 'center', borderRadius: 8, }, boxText: { color: 'white', fontSize: 18, fontWeight: 'bold', }, }); ``` **Old architecture (baseline):** | Android | iOS | | --- | --- | | <img width="503" height="935" alt="android - old arch" src="https://github.com/user-attachments/assets/6051bfac-ae5e-454d-94e3-b632e9062b55" /> | <img width="568" height="1084" alt="ios - old arch" src="https://github.com/user-attachments/assets/8c216962-ccf1-40d7-bd81-227331aaf288" /> | **New architecture (before this PR, iOS diverges):** | Android | iOS | | --- | --- | | <img width="503" height="935" alt="android - new arch" src="https://github.com/user-attachments/assets/35fcc60e-e172-42b8-af54-6360d4425757" /> | <img width="568" height="1084" alt="ios - new arch - not fixed" src="https://github.com/user-attachments/assets/2b558f16-94b5-4f8a-adb6-90f777e69fa0" /> | **New architecture (after this PR, iOS fixed):** | Android | iOS | | --- | --- | | <img width="503" height="935" alt="android - new arch" src="https://github.com/user-attachments/assets/35fcc60e-e172-42b8-af54-6360d4425757" /> | <img width="568" height="1084" alt="ios - new arch - fixed" src="https://github.com/user-attachments/assets/9467f96c-2ef1-46fa-8209-4f8e45b14c21" /> | Reviewed By: javache Differential Revision: D113244783 Pulled By: Abbondanzo fbshipit-source-id: 75edbc2a2a2e1567de12c83375272672cc2683e9
1 parent 41b375e commit 1b92b02

1 file changed

Lines changed: 7 additions & 6 deletions

File tree

  • packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager

packages/react-native/ReactCommon/react/renderer/textlayoutmanager/platform/ios/react/renderer/textlayoutmanager/RCTAttributedTextUtils.mm

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818

1919
using namespace facebook::react;
2020

21-
inline static TextAlignment RCTResolveTextAlignment(TextAlignment textAlignment, LayoutDirection layoutDirection)
21+
inline static TextAlignment RCTResolveTextAlignment(TextAlignment textAlignment, bool isRTL)
2222
{
23-
const bool isRTL = layoutDirection == LayoutDirection::RightToLeft;
2423
switch (textAlignment) {
24+
case TextAlignment::Natural:
25+
return isRTL ? TextAlignment::Right : TextAlignment::Left;
2526
case TextAlignment::Start:
2627
return isRTL ? TextAlignment::Right : TextAlignment::Left;
2728
case TextAlignment::End:
@@ -212,10 +213,10 @@ inline static CGFloat RCTEffectiveFontSizeMultiplierFromTextAttributes(const Tex
212213
// Paragraph Style
213214
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
214215
BOOL isParagraphStyleUsed = NO;
215-
if (textAttributes.alignment.has_value()) {
216-
TextAlignment textAlignment = RCTResolveTextAlignment(
217-
textAttributes.alignment.value_or(TextAlignment::Natural),
218-
textAttributes.layoutDirection.value_or(LayoutDirection::LeftToRight));
216+
const bool isRTL = textAttributes.layoutDirection == LayoutDirection::RightToLeft;
217+
if (textAttributes.alignment.has_value() || isRTL) {
218+
TextAlignment textAlignment =
219+
RCTResolveTextAlignment(textAttributes.alignment.value_or(TextAlignment::Natural), isRTL);
219220

220221
paragraphStyle.alignment = RCTNSTextAlignmentFromTextAlignment(textAlignment);
221222
isParagraphStyleUsed = YES;

0 commit comments

Comments
 (0)