Swift AVFoundation视图更新时发布更新警告排查求助
解决AVFoundation录制代码中“Publishing changes from within view updates is not allowed”警告
警告原因
这个警告不是@StateObject本身导致的,核心问题是:在视图更新周期(比如视图初始化、刷新过程)内,你的CameraViewModel中被@Published标记的属性被修改,触发了状态变更发布,违反了SwiftUI的视图更新规则,从而引发警告。
你的代码里主要风险点集中在两处:
CameraPreview的makeUIView方法中直接修改cameraModel.preview(@Published属性),而makeUIView属于视图初始化流程的一部分,此时修改会触发状态发布。- 如果
checkPermission或setUp方法在视图初始化阶段同步执行,其中对@Published属性的修改也会落入视图更新周期内。
解决方案
1. 避免在视图初始化流程中直接修改ViewModel的@Published属性
方案A:封装预览层创建逻辑到ViewModel
把创建预览层的逻辑放到ViewModel中,不在视图初始化流程中修改@Published属性:
class CameraViewModel: NSObject, ObservableObject, AVCaptureFileOutputRecordingDelegate { // ... 现有属性 // 新增方法:创建预览层,不直接修改@Published属性 func createPreviewLayer(size: CGSize) -> AVCaptureVideoPreviewLayer { let preview = AVCaptureVideoPreviewLayer(session: session) preview.frame.size = size preview.videoGravity = .resizeAspectFill return preview } } // 修改CameraPreview struct CameraPreview: UIViewRepresentable { @EnvironmentObject var cameraModel : CameraViewModel var size: CGSize func makeUIView(context: Context) -> UIView { let view = UIView() // 直接创建预览层添加到视图,不修改ViewModel的@Published属性 let preview = cameraModel.createPreviewLayer(size: size) view.layer.addSublayer(preview) DispatchQueue.global(qos: .userInitiated).async { cameraModel.session.startRunning() } return view } func updateUIView(_ uiView: UIView, context: Context) { } }
方案B:异步修改ViewModel的preview属性
如果必须保留cameraModel.preview的@Published属性,用DispatchQueue.main.async包裹修改操作,避开视图更新周期:
func makeUIView(context: Context) -> UIView { let view = UIView() DispatchQueue.main.async { self.cameraModel.preview = AVCaptureVideoPreviewLayer(session: self.cameraModel.session) self.cameraModel.preview.frame.size = self.size self.cameraModel.preview.videoGravity = .resizeAspectFill view.layer.addSublayer(self.cameraModel.preview) } DispatchQueue.global(qos: .userInitiated).async { self.cameraModel.session.startRunning() } return view }
2. 延迟ViewModel的权限检查与配置操作
确保checkPermission不在视图初始化的同步流程中执行,而是放到视图的onAppear回调中,并异步执行:
struct HomeStory: View { @StateObject var cameraModel = CameraViewModel() var body: some View { ZStack(alignment: .bottom) { CameraPreview(size: UIScreen.main.bounds.size) .environmentObject(cameraModel) } .onAppear { // 异步执行权限检查,避开视图更新周期 DispatchQueue.main.async { cameraModel.checkPermission() } } } }
同时修改checkPermission中的setUp调用,确保配置操作在后台队列执行,UI相关的状态变更切回主线程:
func checkPermission(){ switch AVCaptureDevice.authorizationStatus(for: .video) { case .authorized: DispatchQueue.global(qos: .userInitiated).async { self.setUp() // 若有UI相关状态需要更新,切回主线程 DispatchQueue.main.async { // 比如更新UI标记等操作 } } return case .notDetermined: AVCaptureDevice.requestAccess(for: .video) { (status) in if status{ DispatchQueue.global(qos: .userInitiated).async { self.setUp() } } } case .denied: DispatchQueue.main.async { self.alert.toggle() } return default: return } }
总结
@StateObject是SwiftUI中管理视图生命周期内ObservableObject的正确方式,警告的根源是状态变更的时机不当,而非@StateObject的用法错误。通过异步执行状态修改、调整操作时机,就能彻底消除这个警告。
内容的提问来源于stack exchange,提问作者Ahmed Zaidan
相关产品推荐
相关产品推荐

