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): flickering custom header items #2247

Merged
merged 14 commits into from
Jul 24, 2024
35 changes: 0 additions & 35 deletions apps/src/tests/Test556.js

This file was deleted.

70 changes: 70 additions & 0 deletions apps/src/tests/Test556.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { NavigationContainer, ParamListBase } from '@react-navigation/native';
import { NativeStackNavigationProp, createNativeStackNavigator } from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

type ScreenBaseProps = {
navigation: NativeStackNavigationProp<ParamListBase>;
}

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
animation: 'fade',
}}>
<Stack.Screen name="First" component={First} options={{
headerTitle: () => (
<View style={[styles.container, { backgroundColor: 'goldenrod' }]}>
<Text>Hello there!</Text>
</View>
),
headerRight: () => (
<View style={[styles.container, { backgroundColor: 'lightblue' }]}>
<Text>Right-1</Text>
</View>
),
}} />
<Stack.Screen name="Second" component={Second} options={{
headerTitle: () => (
<View style={[styles.container, { backgroundColor: 'mediumseagreen' }]}>
<Text>General Kenobi</Text>
</View>
),
headerRight: () => (
<View style={[styles.container, { backgroundColor: 'mediumvioletred' }]}>
<Text>Right-2</Text>
</View>
),
}} />
</Stack.Navigator>
</NavigationContainer>
);
}

function First({ navigation }: ScreenBaseProps) {
return (
<Button
title="Tap me for second screen"
onPress={() => navigation.navigate('Second')}
/>
);
}

function Second({ navigation }: ScreenBaseProps) {
return (
<Button
title="Tap me for first screen"
onPress={() => navigation.popTo('First')}
/>
);
}

const styles = StyleSheet.create({
container: {
padding: 3,
},
});
31 changes: 31 additions & 0 deletions ios/RNSScreenStackHeaderConfig.mm
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,8 @@ + (void)updateViewController:(UIViewController *)vc
navitem.titleView = nil;

for (RNSScreenStackHeaderSubview *subview in config.reactSubviews) {
// This code should be kept in sync on Fabric with analogous switch statement in
// `- [RNSScreenStackHeaderConfig replaceNavigationBarViewsWithSnapshotOfSubview:]` method.
switch (subview.type) {
case RNSScreenStackHeaderSubviewTypeLeft: {
#if !TARGET_OS_TV
Expand Down Expand Up @@ -755,10 +757,39 @@ - (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childCompone

- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index
{
// For explanation of why we can make a snapshot here despite the fact that our children are already
// unmounted see https://github.com/software-mansion/react-native-screens/pull/2261
[self replaceNavigationBarViewsWithSnapshotOfSubview:(RNSScreenStackHeaderSubview *)childComponentView];
[_reactSubviews removeObject:(RNSScreenStackHeaderSubview *)childComponentView];
[childComponentView removeFromSuperview];
}

- (void)replaceNavigationBarViewsWithSnapshotOfSubview:(RNSScreenStackHeaderSubview *)childComponentView
{
UINavigationItem *navitem = _screenView.controller.navigationItem;
UIView *snapshot = [childComponentView snapshotViewAfterScreenUpdates:NO];

// This code should be kept in sync with analogous switch statement in
// `+ [RNSScreenStackHeaderConfig updateViewController: withConfig: animated:]` method.
switch (childComponentView.type) {
case RNSScreenStackHeaderSubviewTypeLeft:
navitem.leftBarButtonItem.customView = snapshot;
break;
case RNSScreenStackHeaderSubviewTypeCenter:
case RNSScreenStackHeaderSubviewTypeTitle:
navitem.titleView = snapshot;
break;
case RNSScreenStackHeaderSubviewTypeRight:
navitem.rightBarButtonItem.customView = snapshot;
break;
case RNSScreenStackHeaderSubviewTypeSearchBar:
case RNSScreenStackHeaderSubviewTypeBackButton:
break;
default:
RCTLogError(@"[RNScreens] Unhandled subview type: %ld", childComponentView.type);
}
}

static RCTResizeMode resizeModeFromCppEquiv(react::ImageResizeMode resizeMode)
{
switch (resizeMode) {
Expand Down
Loading