AVFoundation+SwiftUI自定义相机授权后仅显示白屏问题求助
自定义相机白屏及初始化错误问题
我花费数周尝试创建自定义相机,试过多种教程均未成功。目前代码结构看似合理,但在真机上授予相机权限后仅显示白屏。此前在setUp函数的let input = try AVCaptureDeviceInput(device: device)行曾出现致命错误,尝试可选绑定仍未解决。以下是我的代码及权限设置:
App 入口代码
import SwiftUI import FirebaseCore @main struct FirebaseAuthYoutubeApp: App { @StateObject var viewModal = AuthViewModel() init() { FirebaseApp.configure() } var body: some Scene { WindowGroup { /* ContentView() .environmentObject(viewModal) */ CameraView2() } } }
相机实现代码
import SwiftUI import AVFoundation import UIKit struct CamerVIEW2Kavsoft: View { var body: some View { Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) } } struct CamerVIEW2Kavsoft_Previews: PreviewProvider { static var previews: some View { CameraView2() } } struct CameraView2: View { @StateObject var camera = cameraModel() var body: some View{ ZStack{ // 相机预览层 CameraPreview(camera: camera) .ignoresSafeArea(.all, edges: .all) VStack{ if camera.isTaken { HStack{ Spacer() Button(action: camera.retaTake, label: { Image(systemName: "arrow.triangle.2.circlepath.camera") .foregroundColor(.black) .padding() .background(Color.white) .clipShape(Circle()) }) .padding(.trailing,10) } } Spacer() HStack { // 拍照后显示保存和重拍按钮 if camera.isTaken { Button(action: {if !camera.isSaved{camera.savePic()}}, label: { Text(camera.isSaved ? "已保存" : "保存") .foregroundColor(.black) .fontWeight(.semibold) .padding(.vertical,10) .padding(.horizontal,20) .background(Color.white) .clipShape(Capsule()) }) .padding(.leading) Spacer() } else { Button(action: {camera.takePic()}, label: { ZStack{ Circle() .fill(Color.white) .frame(width: 65, height: 65) Circle() .stroke(Color.white, lineWidth: 2) .frame(width: 75, height: 75) } }) } } .frame(height: 75) } } .onAppear(perform: { camera.Check() }) } } // 相机模型类 class cameraModel: NSObject, ObservableObject, AVCapturePhotoCaptureDelegate { @Published var isTaken = false @Published var session = AVCaptureSession() @Published var alert = false // 照片输出对象 @Published var output = AVCapturePhotoOutput() // 预览层 @Published var preview : AVCaptureVideoPreviewLayer! @Published var isSaved = false @Published var picData = Data(count: 0) func Check() { // 检查相机权限 switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: // 权限已授权,初始化会话 setUp() return case .notDetermined: // 未确定权限,请求授权 AVCaptureDevice.requestAccess(for: .video) { (status) in if status { self.setUp() } } case .denied: // 权限被拒绝,弹出提示 self.alert.toggle() return default: return } } func setUp() { // 初始化相机会话 do { // 开始配置会话 self.session.beginConfiguration() // 获取后置双摄像头设备 if let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back) { do { let input = try AVCaptureDeviceInput(device: device) // 检查并添加输入到会话 if self.session.canAddInput(input) { self.session.addInput(input) } } catch { print(error.localizedDescription) } // 添加输出到会话 if self.session.canAddOutput(self.output) { self.session.addOutput(self.output) } self.session.commitConfiguration() } }catch { print(error.localizedDescription) } } // 拍照功能 func takePic(){ DispatchQueue.global(qos: .background).async { self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self) self.session.stopRunning() DispatchQueue.main.async { withAnimation{self.isTaken.toggle()} self.isSaved = false } } } // 重拍功能 func retaTake() { DispatchQueue.global(qos: .background).async { self.session.startRunning() DispatchQueue.main.async { withAnimation{self.isTaken.toggle()} } } } func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { if error != nil{ return } print("照片已拍摄...") guard let imageData = photo.fileDataRepresentation() else {return} self.picData = imageData } // 保存照片到相册 func savePic() { let image = UIImage(data: self.picData)! UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) self.isSaved = true print("保存成功") } } // 相机预览视图 struct CameraPreview: UIViewRepresentable { @ObservedObject var camera : cameraModel func makeUIView(context: Context) -> UIView { let view = UIView(frame: UIScreen.main.bounds) camera.preview = AVCaptureVideoPreviewLayer(session: camera.session) camera.preview.frame = view.frame camera.preview.videoGravity = .resizeAspectFill view.layer.addSublayer(camera.preview) // 启动会话 camera.session.startRunning() return view } func updateUIView(_ uiView: UIView, context: Context) { } }
Info.plist 权限设置

问题解决方法
设备适配问题:
部分设备无builtInDualCamera(如单摄像头iPhone SE),需替换为通用后置摄像头获取逻辑:// 替换原设备获取代码 guard let device = AVCaptureDevice.default(.video, for: .video, position: .back) ?? AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { print("无法获取后置摄像头") return }初始化致命错误修复:
添加设备配置锁定逻辑,避免设备状态异常导致的初始化失败:do { try device.lockForConfiguration() device.activeVideoMinFrameDuration = CMTimeMake(value: 1, timescale: 30) device.activeVideoMaxFrameDuration = CMTimeMake(value: 1, timescale: 30) device.unlockForConfiguration() let input = try AVCaptureDeviceInput(device: device) if self.session.canAddInput(input) { self.session.addInput(input) } } catch { print("输入创建失败: \(error.localizedDescription)") }会话启动时机调整:
移除CameraPreview中的会话启动代码,在setUp配置完成后启动:// 在setUp函数的session.commitConfiguration()后添加 DispatchQueue.global(qos: .userInitiated).async { self.session.startRunning() }
内容的提问来源于stack exchange,提问作者Carl Rudling
相关产品推荐
相关产品推荐

