如何用Capacitor/Swift/Kotlin同时拍摄前后置摄像头照片(仿BeReal)
解决Capacitor/原生实现同时调用前后置摄像头(单次权限授权)
Capacitor 优化方案
你遇到的重复权限弹窗问题,根源在于两次调用Camera.getPhoto时,每次都会触发系统权限检查。只需提前手动申请一次相机权限,后续调用拍照接口就不会再弹授权窗。
优化后代码
import { Camera, CameraDirection, CameraResultType, CameraSource, CameraPermissions } from '@capacitor/camera'; const getDualPhotos = async () => { // 提前检查并请求相机权限,仅触发一次弹窗 const permissionStatus = await Camera.checkPermissions(); if (permissionStatus.camera !== 'granted') { const requestResult = await Camera.requestPermissions(); if (requestResult.camera !== 'granted') { throw new Error('相机权限未授权,无法拍照'); } } // 并行调用前后置拍照,不再触发权限检查 const [rearPhoto, frontPhoto] = await Promise.all([ Camera.getPhoto({ quality: 90, allowEditing: false, source: CameraSource.Camera, resultType: CameraResultType.Base64, direction: CameraDirection.Rear }), Camera.getPhoto({ quality: 90, allowEditing: false, source: CameraSource.Camera, resultType: CameraResultType.Base64, direction: CameraDirection.Front }) ]); // 处理两张照片数据 console.log('后置照片Base64:', rearPhoto.base64String); console.log('前置照片Base64:', frontPhoto.base64String); };
关键说明
- 相机权限本身已包含访问前后置摄像头的权限,无需分别申请
- 用
Promise.all并行调用拍照接口,比串行调用效率更高
原生实现方案(复刻BeReal同步拍摄逻辑)
如果需要更精准的同步拍摄控制(比如实时双摄像头预览),可以直接用原生代码实现:
Swift(iOS)实现
import UIKit import AVFoundation class DualCameraHandler: NSObject, AVCapturePhotoCaptureDelegate { private var rearSession: AVCaptureSession! private var frontSession: AVCaptureSession! private var rearPhotoOutput: AVCapturePhotoOutput! private var frontPhotoOutput: AVCapturePhotoOutput! // 请求相机权限并初始化双摄像头 func setupDualCameras() { AVCaptureDevice.requestAccess(for: .video) { granted in guard granted else { return } DispatchQueue.main.async { self.configureRearCamera() self.configureFrontCamera() } } } // 配置后置摄像头 private func configureRearCamera() { rearSession = AVCaptureSession() rearSession.sessionPreset = .photo guard let rearDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return } do { let input = try AVCaptureDeviceInput(device: rearDevice) if rearSession.canAddInput(input) { rearSession.addInput(input) } rearPhotoOutput = AVCapturePhotoOutput() if rearSession.canAddOutput(rearPhotoOutput) { rearSession.addOutput(rearPhotoOutput) } rearSession.startRunning() } catch { print("后置摄像头配置失败:\(error)") } } // 配置前置摄像头 private func configureFrontCamera() { frontSession = AVCaptureSession() frontSession.sessionPreset = .photo guard let frontDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front) else { return } do { let input = try AVCaptureDeviceInput(device: frontDevice) if frontSession.canAddInput(input) { frontSession.addInput(input) } frontPhotoOutput = AVCapturePhotoOutput() if frontSession.canAddOutput(frontPhotoOutput) { frontSession.addOutput(frontPhotoOutput) } frontSession.startRunning() } catch { print("前置摄像头配置失败:\(error)") } } // 同步拍摄前后置照片 func captureDualPhotos() { let rearSettings = AVCapturePhotoSettings() let frontSettings = AVCapturePhotoSettings() rearPhotoOutput.capturePhoto(with: rearSettings, delegate: self) frontPhotoOutput.capturePhoto(with: frontSettings, delegate: self) } // 处理拍摄结果 func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { if let error = error { print("拍照失败:\(error)") return } let isRear = output == rearPhotoOutput if let imageData = photo.fileDataRepresentation() { // 可将图片数据转为UIImage或保存到本地 if isRear { print("后置照片已获取") } else { print("前置照片已获取") } } } }
Kotlin(Android)实现
import android.Manifest import android.content.pm.PackageManager import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.camera.core.CameraSelector import androidx.camera.core.ImageCapture import androidx.camera.core.ImageCaptureException import androidx.camera.lifecycle.ProcessCameraProvider import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.google.common.util.concurrent.ListenableFuture import java.util.concurrent.ExecutorService import java.util.concurrent.Executors class DualCameraActivity : AppCompatActivity() { private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider> private lateinit var cameraExecutor: ExecutorService private var rearImageCapture: ImageCapture? = null private var frontImageCapture: ImageCapture? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) cameraExecutor = Executors.newSingleThreadExecutor() cameraProviderFuture = ProcessCameraProvider.getInstance(this) // 检查并请求相机权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), 1001) } else { setupDualCameras() } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == 1001 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { setupDualCameras() } } // 配置双摄像头 private fun setupDualCameras() { cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() // 绑定后置摄像头 val rearCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() rearImageCapture = rearCapture try { cameraProvider.bindToLifecycle( this, CameraSelector.DEFAULT_BACK_CAMERA, rearCapture ) } catch (e: Exception) { print("后置摄像头绑定失败:${e.message}") } // 绑定前置摄像头 val frontCapture = ImageCapture.Builder() .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY) .build() frontImageCapture = frontCapture try { cameraProvider.bindToLifecycle( this, CameraSelector.DEFAULT_FRONT_CAMERA, frontCapture ) } catch (e: Exception) { print("前置摄像头绑定失败:${e.message}") } }, ContextCompat.getMainExecutor(this)) } // 同步拍摄两张照片 fun captureDualPhotos() { rearImageCapture?.let { capture -> val outputOptions = ImageCapture.OutputFileOptions.Builder(createTempFile()).build() capture.takePicture( outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { print("后置照片已保存:${outputFileResults.savedUri}") } override fun onError(exc: ImageCaptureException) { print("后置拍照失败:${exc.message}") } } ) } frontImageCapture?.let { capture -> val outputOptions = ImageCapture.OutputFileOptions.Builder(createTempFile()).build() capture.takePicture( outputOptions, cameraExecutor, object : ImageCapture.OnImageSavedCallback { override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { print("前置照片已保存:${outputFileResults.savedUri}") } override fun onError(exc: ImageCaptureException) { print("前置拍照失败:${exc.message}") } } ) } } override fun onDestroy() { super.onDestroy() cameraExecutor.shutdown() } }
核心逻辑总结
- 系统相机权限是统一授权,无需为前后置摄像头分别申请
- Capacitor插件的
getPhoto方法默认每次调用都会触发权限检查,提前手动申请即可避免重复弹窗 - 原生实现可直接控制双摄像头会话,实现真正的同步拍摄,适合对延迟要求高的场景
内容的提问来源于stack exchange,提问作者user82395214
相关产品推荐
相关产品推荐

