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

使用Aespa库时,如何在updateUIViewController中正确更新frame?

解决Aespa相机预览Frame不稳定的问题

问题核心在于原库用DispatchQueue.main.async异步设置previewLayer.frame的方式不可靠——updateUIViewController调用时,UIKit控制器的view bounds还未同步到SwiftUI布局的最终尺寸,而异步任务的执行顺序又受主队列任务影响,导致不同场景下尺寸不一致。

正确的实现方式:监听视图布局完成事件

UIKit中,viewDidLayoutSubviews是专门处理视图布局稳定后的回调,每次视图bounds确定后都会触发,是更新子视图frame的标准时机。我们可以通过自定义托管ViewController来替代原库的默认实现:

1. 自定义预览托管控制器

class PreviewHostViewController: UIViewController {
    var previewLayer: AVCaptureVideoPreviewLayer!
    var gravity: AVLayerVideoGravity = .resizeAspect
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        // 布局完成后,直接用当前view的bounds设置预览层尺寸
        previewLayer.frame = view.bounds
    }
}

2. 修改UIViewControllerRepresentable实现

替换原库的Preview结构体实现,将预览层的frame更新逻辑移到viewDidLayoutSubviews中:

struct FixedPreview: UIViewControllerRepresentable {
    let previewLayer: AVCaptureVideoPreviewLayer
    var gravity: AVLayerVideoGravity = .resizeAspect
    
    func makeUIViewController(context: Context) -> PreviewHostViewController {
        let vc = PreviewHostViewController()
        vc.previewLayer = previewLayer
        vc.gravity = gravity
        vc.view.layer.addSublayer(previewLayer)
        return vc
    }
    
    func updateUIViewController(_ uiViewController: PreviewHostViewController, context: Context) {
        // 仅更新视频重力属性,frame交给布局回调处理
        uiViewController.previewLayer.videoGravity = gravity
    }
}

3. 替换原有使用方式

如果不想修改Aespa库源码,可以自己封装一层,在ViewModel中返回自定义的FixedPreview:

@Observable
class ViewModel {
    @ObservationIgnored var aespaSession = Aespa.session(with: AespaOption(albumName: nil))
    @ObservationIgnored var preview: FixedPreview {
        let layer = aespaSession.interactivePreviewLayer(...) // 获取原预览层
        return FixedPreview(previewLayer: layer, gravity: .resizeAspectFill)
    }
    var show = false
}

为什么这能解决问题?

  • viewDidLayoutSubviews会在SwiftUI完成布局传递、UIKit视图bounds稳定后触发,能保证拿到的是最终的尺寸。
  • 去掉异步派发后,不再依赖主队列的任务执行顺序,无论父视图布局如何变化(比如zIndex调整、屏幕旋转),预览层都会自动适配正确的frame,让Preview().frame(width: 350, height: 500)的设置稳定生效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 02:15:14