iOS app open/close transition shows shadow on SegmentedButtons
Current behaviour
I am rendering a Segmented Button (basic, vanilla implementation) When I open or close the iOS app, I see a gradient within one of the segments.
Expected behaviour
No gradient.
How to reproduce?
Use iOS Add a segmented button to your view. Close or open the iOS app to show the gradient.
Preview
Implementation:
<SegmentedButtons value={buttonValue} onValueChange={setButtonValue} buttons={[ { value: 'consumed', label: 'Consumed', showSelectedCheck: true }, { value: 'remaining', label: 'Remaining', showSelectedCheck: true }, ]} density="small" />
Screenshot:
What have you tried so far?
Explicitly setting a background color with
style={[styles.group, { backgroundColor: theme.colors.background }]}
Your Environment
| software | version |
|---|---|
| ios | 16.0 |
| react-native | 0.76.3 |
| react-native-paper | ^5.12.5 |
@astrojams1 Yes I am also facing same issue . SegmentedButtons may get re-evaluated when the app becomes active again, leading iOS to recreate the layers dynamically, sometimes causing unintended visual effects.
I believe that as a work around you can add activity indicator whenever the app comes from background to foreground.
Please find the code below :
import React, { useState, useEffect, useRef } from 'react'; import { AppState, View, ActivityIndicator } from 'react-native'; import { SegmentedButtons } from 'react-native-paper';
const MySegmentedControl = () => { const appState = useRef(AppState.currentState); const selectedValue = useRef('first'); // Avoid unnecessary re-renders const [isLoading, setIsLoading] = useState(false); // Controls Activity Indicator
useEffect(() => { const handleAppStateChange = (nextAppState) => { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { // Show the loader and refresh SegmentedButtons setIsLoading(true); setTimeout(() => setIsLoading(false), 500); // Simulate a smooth refresh } appState.current = nextAppState; };
const subscription = AppState.addEventListener('change', handleAppStateChange);
return () => subscription.remove();
}, []);
return ( <View style={{ padding: 20, alignItems: 'center' }}> {isLoading ? ( <ActivityIndicator size="large" color="#6200EE" /> // React Native Paper theme color ) : ( <SegmentedButtons value={selectedValue.current} onValueChange={(val) => (selectedValue.current = val)} buttons={[ { value: 'first', label: 'First' }, { value: 'second', label: 'Second' }, { value: 'third', label: 'Third' }, ]} style={{ backgroundColor: 'white' }} // Ensures no transparency issues /> )} </View> ); };
export default MySegmentedControl;
I am seeing this with "^5.14.0", with the additions:
- The drop shadow always shows on an iPhone 15 with iOS 18.4.1, whether animating or not
- The drop shadow shows only while animating (like demonstrated by the OP) on an iPhone 16e with iOS 18.3.1
- The drop shadow shows in the iOS Simulator on the iPhone 16 Plus (and I'm sure other devices) with iOS 18.1 if I double tap the home button (view the currently running app list) and it disappears after the app refocuses
- The drop shadow shows in the iOS Simulator on the iPad (10th generation) with iOS 18.1 if I double tap the home button and it disappears after the app refocuses
I've updated to "^5.14.5" but haven't put out a TestFlight build. Based on the OP using the latest version, I don't think I'll see anything different.
This behavior is weird
When went what wrong:
- On simulator/device it only occurs only when bringing the app from the background to the foreground
- On my phsyical iPad the shadow keeps visible when build for production
- Only the last button is affected (If there are three SegmentedButtons the shadow is only at the last one)
- Workaround set outline to any transparent color
theme={{ colors: { outline: '#ffffff00' } }}
This seems to be an issue with expo/react-native -> https://github.com/facebook/react-native/issues/49442 I have created a minimal example: https://snack.expo.dev/@m.bahl/2e0d5d