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]; } }