常见问题
按症状 → 原因 → 解法排查。多数问题集中在「缺 holder」「缺 peerDeps」「不在真机上跑」三类。
症状:相机打开后画面全黑 / api.open() 没反应
两个最常见原因,逐一排查:
因 1:holder 没渲染进树 → UI 不挂载且 Promise 持续 pending
useCamera() 返回的 holder 必须出现在 React 树里。缺少它时,合法 api.open() 仍会创建会话并返回 Promise,但相机 Container 没有挂载,所以 UI 不弹、拍摄流程也无法完成该 Promise。Promise 会保持 pending,直到 api.close()、后续合法 open() 或 Hook 卸载取消它。
// ❌ Incorrect:拿了 holder 却没放进树
const [api, holder] = useCamera();
return <Button title="拍照" onPress={() => api.open(cfg)} />; // 会话创建,但 Promise 持续 pending
// ✅ Correct:holder 必须在 React 树里(位置不限)
const [api, holder] = useCamera();
return (
<View>
<Button title="拍照" onPress={() => api.open(cfg)} />
{holder}
</View>
);
因 2:缺权限声明(画面黑但模态弹出了)
模态弹出但取景画面全黑,通常是缺权限键:
- iOS ——
ios/<App>/Info.plist缺NSCameraUsageDescription;只有使用录像模式时还需NSMicrophoneUsageDescription。 - Android ——
android/app/src/main/AndroidManifest.xml缺android.permission.CAMERA;只有使用录像模式时还需RECORD_AUDIO。
本库只返回 App 临时目录中的拍摄文件,不读写系统相册,因此不会无条件要求 NSPhotoLibraryAddUsageDescription 或 READ_MEDIA_IMAGES。若 App 另有保存 / 选择相册内容的业务,再按那项能力单独配置。
补齐后重新编译(iOS 还要 pod install)。完整权限键见安装 → 权限配置。若权限本身被用户拒绝,api.open() 会 resolve code: 403,按下文处理。
症状:打包 / 运行报 Unable to resolve module ...
缺同伴包。缺少必要的 peer 依赖会导致模块解析或运行失败,最常漏的是 react-native-vision-camera-worklets。
Unable to resolve module react-native-vision-camera-worklets
# ❌ Incorrect:只装相机引擎,缺 worklets
yarn add @unif/react-native-camera react-native-vision-camera
# ✅ Correct:补 worklets(版本与 vision-camera 对齐,同为 ^5.x)
yarn add react-native-vision-camera-worklets
cd ios && bundle exec pod install
vision-camera 5.x 内部对 react-native-vision-camera-worklets 做了懒 require,Metro 在静态分析阶段就会解析它。即使本库不使用 Frame Processor,缺这个包打包期也会报 Unable to resolve module react-native-vision-camera-worklets,运行时报 Cannot use Frame Processors - react-native-vision-camera-worklets is not installed。
其他 Unable to resolve / 原生符号缺失
逐项核对安装 → 完整 peer 清单是否装齐。两个易错点:
- 文件系统装错包 —— 本库用 fork
@dr.pogodin/react-native-fs,不是react-native-fs。装错或两者并存会冲突,先卸react-native-fs再装 fork。 - 安装本库或升级原生包后没
pod install—— 本库自身、vision-camera / Skia / fs / video 都含原生代码,iOS 必须重新cd ios && bundle exec pod install,否则报原生模块或符号缺失。
症状:照片上没出现水印
因 1:对录像加水印(水印仅照片)
// ❌ Incorrect:期望 video 出水印
await api.open({
cameraMode: [{ mode: 'video' }],
dataRetainedMode: 'clear',
watermark: { content: ['现场'] },
}); // 录像不会有水印
水印仅对照片(image/jpeg)生效,录像(video/mp4)没有水印。 这是设计行为。
因 2:缺水印依赖
Skia 负责取景器水印预览,RNFS 负责 session 临时路径/清理;成片由本库原生文件处理器直接写 JPEG。peer 缺失或安装本库后未重新原生构建都会失败:
yarn add @shopify/react-native-skia @dr.pogodin/react-native-fs
cd ios && bundle exec pod install
设备协商输出需精裁或可见水印处理失败时,不会返回 raw / 半成品并以
200成功;相机会保留当前 session 与此前文件,提示“照片处理失败,请重试”。水印仍只是可视标记,不是防篡改手段。用法见指南 → 水印。
症状:相机 / 水印在模拟器或浏览器里跑不起来
✅ 这是预期行为,不是 bug。 vision-camera 依赖真实相机硬件;模拟器可验证部分界面/原生编译,却不能证明真实照片输出、相机 IOSurface 或旧设备峰值内存。完整相机链路请始终在真机上验证。
不要在模拟器里测真实拍摄。单元测试用测试(Mock)页的 jest.mock 方案,在无硬件环境跑通拍照流程逻辑。
处理 api.open() 的 result code
正常渲染 holder 后,每个 api.open() 会话都会以 CameraResult resolve(取消也不 reject),按 code 兜底。若缺少 holder,合法调用会因 Container 未挂载而保持 pending,需由 close()、后续合法 open() 或 Hook 卸载取消:
const res = await api.open(cfg);
switch (res.code) {
case 200:
use(res.data);
break; // 成功:取文件
case 0:
/* 用户取消,静默 */ break;
case 403:
/* 无权限:引导去系统设置 */ break;
case 404:
/* 无摄像设备:提示不支持 */ break;
case 500:
/* 配置非法(必填项或可选字段未通过运行时校验)*/ break;
case 503:
/* 保留码,当前不触发(录像失败走相机内重试)*/ break;
}
// ❌ Incorrect:把 0 当成功 —— 取消时 data 为空
if (res.code === 0) use(res.data);
// ✅ Correct:只有 200 是成功
if (res.code === 200) use(res.data);
各 code 含义见核心概念 → result code。
iOS:pod install 报 LICENSE 警告
[!] The `...` pod ... has a license ... which doesn't provide any official binaries...
先区分 LICENSE 提示与安装错误;结合 pod install 的退出状态、实际依赖和后续编译结果判断。此提示本身不代表设备行为已通过验证。
拍照成功后,文件会自动永久保存吗?
不会。code === 200 表示用户确认了临时媒体。应用需要保存或上传以长期保留;可预览的 URI 不等于已写入相册,也不表示业务请求成功。文件责任见调用与资源。