You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 05:07:01