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

Swift中AVFoundation自定义相机启动先放大后恢复正常问题排查

问题分析与解决方案

你遇到的相机启动时先放大再恢复正常的问题,核心原因是预览层与相机会话不同步,加上会话初始化时机的问题导致视觉跳变,以下是具体排查和修复步骤:

1. 预览层初始化过早

你在CameraViewModel的init方法中就创建了preview层,但此时相机会话(session)还未经过setUp()配置,预览层一开始绑定的是一个空会话。当后续setUp()完成会话配置并启动后,预览层才会重新适配真实的相机输入,这就导致了视觉上的缩放跳变。

修复:延迟预览层初始化

修改CameraViewModel的初始化逻辑,等待会话配置完成后再创建预览层:

override init() {
    super.init()
    self.checkPermission()
    // 移除原有的preview初始化代码,挪到setUp完成后
}

func setUp(){
    do {
        self.session.beginConfiguration()
        let cameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
        // 强制锁定相机缩放为1.0,排除设备默认缩放异常
        try cameraDevice!.lockForConfiguration()
        cameraDevice!.videoZoomFactor = 1.0
        cameraDevice!.unlockForConfiguration()
        
        let videoInput = try AVCaptureDeviceInput(device: cameraDevice!)
        let audioDevice = AVCaptureDevice.default(for: .audio)
        let audioInput = try AVCaptureDeviceInput(device: audioDevice!)
        
        if self.session.canAddInput(videoInput) && self.session.canAddInput(audioInput){
            self.session.addInput(videoInput)
            self.session.addInput(audioInput)
        }

        if self.session.canAddOutput(self.output){
            self.session.addOutput(self.output)
        }
        
        if self.session.canAddOutput(self.photoOutput) {
            self.session.addOutput(self.photoOutput)
        }
        
        self.session.commitConfiguration()
        
        // 会话配置完成后,在主线程初始化预览层
        DispatchQueue.main.async {
            self.preview = AVCaptureVideoPreviewLayer(session: self.session)
            self.preview.videoGravity = .resizeAspectFill
        }
        
        // 配置完成后启动会话,避免提前启动导致的异常
        DispatchQueue.global(qos: .userInitiated).async {
            if !self.session.isRunning {
                self.session.startRunning()
            }
        }
    }
    catch {
        print(error.localizedDescription)
    }
}

2. 预览层未响应会话更新

原CameraPreview的updateUIView方法是空的,当preview层更新时,UIView无法同步刷新,导致旧的预览层残留或适配错误。

修复:在updateUIView中同步预览层

修改CameraPreview的updateUIView方法,确保预览层随cameraModel.preview的变化而更新:

func updateUIView(_ uiView: UIView, context: Context) {
    guard let preview = cameraModel.preview else { return }
    
    // 移除旧的预览层,避免叠加
    uiView.layer.sublayers?.filter { $0 is AVCaptureVideoPreviewLayer }.forEach { $0.removeFromSuperlayer() }
    
    preview.frame = uiView.bounds
    preview.videoGravity = .resizeAspectFill
    uiView.layer.addSublayer(preview)
}

3. 会话启动时机优化

原代码在CameraPreview的makeUIView中启动会话,此时会话可能还未完成配置。将会话启动逻辑挪到setUp方法的末尾,确保配置完成后再启动,避免会话状态不稳定导致的预览异常。

完成以上修改后,相机启动时就会直接加载正确的预览画面,不会出现先放大再恢复的跳变问题。

内容的提问来源于stack exchange,提问作者Ahmed Zaidan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 03:01:07