Expo相机视图卸载时应用崩溃问题求助
Expo Camera页面切换崩溃解决方案
问题场景
使用Stack Navigator管理首页标签页与相机页面,已为相机页面配置unmountOnBlur: true,但在两个页面间反复切换时,iOS端出现崩溃,核心错误信息:
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <CALayerArray: 0x281983390> was mutated while being enumerated.'
堆栈追踪确认问题出在-[EXCamera removeFromSuperview]方法,属于相机视图卸载时CALayer枚举与修改的并发冲突。
原始导航代码
function BottomNavigationBar() { return ( <NavigationContainer> <StackNavigator.Navigator screenOptions={{ style: {backgroundColor: NAV_BAR_BACKGROUND_COLOR}, headerShown: false, }} > <StackNavigator.Screen name="Home" component={HomeTabs}/> <StackNavigator.Screen name="Camera" component={CameraScreen} options={{unmountOnBlur: true}}/> </StackNavigator.Navigator> </NavigationContainer> ) }
解决方案
1. 手动控制相机资源销毁时机
在CameraScreen组件中,利用useEffect清理函数,在组件卸载前主动停止相机预览,避免原生层异步操作与组件卸载冲突:
import { Camera } from 'expo-camera'; import { useEffect, useRef } from 'react'; function CameraScreen() { const cameraRef = useRef(null); useEffect(() => { return () => { // 组件卸载前主动停止相机相关操作 if (cameraRef.current) { cameraRef.current.pausePreview(); cameraRef.current.stopPreview(); // 若使用录制功能,需额外停止录制 // cameraRef.current.stopRecording(); } }; }, []); return ( <Camera ref={cameraRef} style={{flex: 1}} /> ); }
2. 延迟组件卸载(规避异步清理冲突)
通过自定义逻辑给相机资源预留清理时间,修改CameraScreen组件:
import { useFocusEffect } from '@react-navigation/native'; import { useState } from 'react'; function CameraScreen() { const [isReadyToUnmount, setIsReadyToUnmount] = useState(true); useFocusEffect( React.useCallback(() => { return () => { // 离开页面时标记未准备好卸载,延迟后再允许卸载 setIsReadyToUnmount(false); setTimeout(() => { setIsReadyToUnmount(true); }, 200); }; }, []) ); // 未准备好卸载时渲染空视图,避免原生层冲突 if (!isReadyToUnmount) { return null; } return ( <Camera style={{flex: 1}} /> ); }
保留unmountOnBlur: true配置确保组件最终会被卸载。
3. 升级Expo Camera版本
该问题属于Expo Camera旧版本的原生层已知bug,升级到最新稳定版本可修复:
expo install expo-camera@latest
4. 替换导航策略(备选)
若上述方法无效,将相机页面改为模态弹窗形式,避免Stack Navigator频繁卸载/挂载组件:
import { Modal, Button } from 'react-native'; import { Camera } from 'expo-camera'; import { useState } from 'react'; function HomeTabs() { const [showCamera, setShowCamera] = useState(false); return ( <> {/* 首页原有内容 */} <Button title="打开相机" onPress={() => setShowCamera(true)} /> <Modal visible={showCamera} animationType="slide"> <Camera style={{flex: 1}} /> <Button title="关闭相机" onPress={() => setShowCamera(false)} /> </Modal> </> ); }
原理说明
崩溃根源是iOS原生层在枚举CALayer数组时,相机视图的removeFromSuperview方法修改了该数组,触发了NSGenericException。通过主动控制资源释放、延迟卸载或升级依赖,均可避免该并发操作冲突。
内容的提问来源于stack exchange,提问作者219CID
相关产品推荐
相关产品推荐

