From 4546d0c06443428397b3a33bda2f08502f997ad6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alex=20Du=C5=BCy?=
<91994767+alduzy@users.noreply.github.com>
Date: Fri, 19 Jul 2024 15:38:24 +0200
Subject: [PATCH] fix(iOS): header left and right layout on fabric (#2248)
## Description
This PR fixes the header layout mismatch when updating both `headerLeft`
and `headerRight` options simultaneously using `navigation.setOptions`.
Fixes #2231 .
## Changes
- Forced subview to re-layout when updating layoutMetrics
- added `Test2231.tsx` repro
## Screenshots / GIFs
### Before
![Screenshot 2024-07-16 at 12 05
14](https://github.com/user-attachments/assets/37a5a77d-1cd5-457f-901e-9dd5b4065a8f)
### After
![Screenshot 2024-07-16 at 12 04
49](https://github.com/user-attachments/assets/025db807-ef6d-46f2-b4c0-cd9f06e03d93)
## Test code and steps to reproduce
- Use `Test2231.tsx` repro
## Checklist
- [x] Ensured that CI passes
---
apps/src/tests/Test2231.tsx | 56 ++++++++++++++++++++++++++++++
apps/src/tests/index.ts | 1 +
ios/RNSScreenStackHeaderSubview.mm | 3 ++
3 files changed, 60 insertions(+)
create mode 100644 apps/src/tests/Test2231.tsx
diff --git a/apps/src/tests/Test2231.tsx b/apps/src/tests/Test2231.tsx
new file mode 100644
index 0000000000..9449ffc3ff
--- /dev/null
+++ b/apps/src/tests/Test2231.tsx
@@ -0,0 +1,56 @@
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import React, { useLayoutEffect, useState } from 'react';
+import { View } from 'react-native';
+
+import { SettingsSwitch, Square } from '../shared';
+import { NavigationContainer } from '@react-navigation/native';
+
+const SettingsScreen = ({ navigation }: any) => {
+ const [hasLeftItem, setHasLeftItem] = useState(false);
+
+ const square1 = (props: { tintColor?: string }) => (
+
+ {hasLeftItem && }
+
+
+ );
+
+ const square2 = (props: { tintColor?: string }) => (
+
+ );
+
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ headerRight: square1,
+ headerTitle: undefined,
+ headerLeft: hasLeftItem ? square2 : undefined,
+ headerBackTitleVisible: false,
+ });
+ }, [navigation, hasLeftItem]);
+
+ return (
+
+ );
+};
+
+const Stack = createNativeStackNavigator();
+
+const App = () => (
+
+
+
+
+
+);
+
+export default App;
diff --git a/apps/src/tests/index.ts b/apps/src/tests/index.ts
index e0b13ffa17..9233d3ba8b 100644
--- a/apps/src/tests/index.ts
+++ b/apps/src/tests/index.ts
@@ -104,5 +104,6 @@ export { default as Test2184 } from './Test2184';
export { default as Test2223 } from './Test2223';
export { default as Test2227 } from './Test2227';
export { default as Test2229 } from './Test2229';
+export { default as Test2231 } from './Test2231';
export { default as TestScreenAnimation } from './TestScreenAnimation';
export { default as TestHeader } from './TestHeader';
diff --git a/ios/RNSScreenStackHeaderSubview.mm b/ios/RNSScreenStackHeaderSubview.mm
index c23c7b301d..35212e92d8 100644
--- a/ios/RNSScreenStackHeaderSubview.mm
+++ b/ios/RNSScreenStackHeaderSubview.mm
@@ -72,6 +72,9 @@ - (void)updateLayoutMetrics:(const react::LayoutMetrics &)layoutMetrics
self);
} else {
self.bounds = CGRect{CGPointZero, frame.size};
+ // We're forcing the parent view to layout this subview with correct frame size,
+ // see: https://github.com/software-mansion/react-native-screens/pull/2248
+ [self.superview layoutIfNeeded];
}
}