我有一些代码下面使用AVFoundation录制视频.当我的应用程序启动时,我在"@StateObject var CameraModel=CameraViewModel()"行收到警告"不允许从视图更新中发布更改,这将导致未定义的行为".我试着调试这个,但找不到方法来摆脱它.这与使用@StateObject有关吗?

import SwiftUI
import SwiftUI
import AVKit
import AVFoundation

struct HomeStory: View {
  @StateObject var cameraModel = CameraViewModel()
  
  var body: some View {
      ZStack(alignment: .bottom) {
      }
  }
}

struct CameraPreview: UIViewRepresentable {
  @EnvironmentObject var cameraModel : CameraViewModel
  var size: CGSize
  
  func makeUIView(context: Context) ->  UIView {
      let view = UIView()
      
      cameraModel.preview = AVCaptureVideoPreviewLayer(session: cameraModel.session)
      cameraModel.preview.frame.size = size
      
      cameraModel.preview.videoGravity = .resizeAspectFill
      view.layer.addSublayer(cameraModel.preview)
      
      DispatchQueue.global(qos: .userInitiated).async {
          cameraModel.session.startRunning()
      }
      
      return view
  }
  
  func updateUIView(_ uiView: UIView, context: Context) { }
}


class CameraViewModel: NSObject, ObservableObject, AVCaptureFileOutputRecordingDelegate {
  @Published var session = AVCaptureSession()
  @Published var alert = false
  @Published var output = AVCaptureMovieFileOutput()
  @Published var preview : AVCaptureVideoPreviewLayer!
  @Published var isRecording: Bool = false
  @Published var recordedURLs: [URL] = []
  @Published var previewURL: URL?
  @Published var showPreview: Bool = false
  @Published var recordedDuration: CGFloat = 0
  @Published var maxDuration: CGFloat = 20
  
  func checkPermission(){
      
      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()
          let cameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
          let videoInput = try AVCaptureDeviceInput(device: cameraDevice!)
          let audioDevice = AVCaptureDevice.default(for: .audio)
          let audioInput = try AVCaptureDeviceInput(device: audioDevice!)
          
          // MARK: Audio Input
          
          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)
          }
          
          self.session.commitConfiguration()
      }
      catch {
          print(error.localizedDescription)
      }
  }
  
  func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {

  }
  
  func mergeVideos(assets: [AVURLAsset],completion: @escaping (_ exporter: AVAssetExportSession)->()) async {

  }
}

推荐答案

根据 comments ,主要的问题是修改@Published propertiesCameraViewModel你里面的makeUIView方法的CameraPreview.这在SwiftUI中通常是不鼓励的,因为它可能会导致意外的行为或警告.

您可能会考虑将相机设置代码从makeUIView移至初始值CameraViewModel.这将确保在创建视图模型时而不是渲染视图时配置相机.

由于启动相机会话可能很耗时,并且可能会阻塞主线程,因此在后台执行此操作是正确的.但是,此操作导致的任何UI更新或状态更改都应调度回主线程.

如果视图模型的preview属性不需要被任何SwiftUI视图观察以进行更改,则不需要标记为@Published.这将阻止SwiftUI在此属性更改时try 重新呈现视图.

那么CameraViewModel就是:

class CameraViewModel: NSObject, ObservableObject, AVCaptureFileOutputRecordingDelegate {
    @Published var session = AVCaptureSession()
    // Other @Published properties

    var preview: AVCaptureVideoPreviewLayer?

    override init() {
        super.init()
        self.setupCamera()
    }

    private func setupCamera() {
        // Setup your camera here, similar to your existing setUp() method.
        // Do not forget to check and request permission if necessary.
    }

    // Rest of your code
}

CameraPreview UIViewRepresentable:

struct CameraPreview: UIViewRepresentable {
    @EnvironmentObject var cameraModel : CameraViewModel
    var size: CGSize
    
    func makeUIView(context: Context) -> UIView {
        let view = UIView(frame: CGRect(origin: .zero, size: size))
        guard let preview = cameraModel.preview else { return view }

        preview.frame = view.bounds
        preview.videoGravity = .resizeAspectFill
        view.layer.addSublayer(preview)

        DispatchQueue.global(qos: .userInitiated).async {
            if !self.cameraModel.session.isRunning {
                self.cameraModel.session.startRunning()
            }
        }
        
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) { }
}

The preview layer is prepared during the view model's initialization and only added to the view in makeUIView.
The session start is kept on a background thread to avoid freezing the UI, but make sure any UI updates or state changes triggered by background operations are dispatched to the main thread.

Ios相关问答推荐

SwiftUI.从自定义视图修改器访问自定义视图子视图

在许多UIButton中对齐文本,即使SF Symbols(或其他图像)具有不同的宽度?

底部导航栏在iOS上浮动

如何在expo-react native中从ios平台中排除包

在 Swift 项目中使用情节提要加载 obj-c 文件

在左侧显示多行值时会出现 SwiftUI 错误,这会 destruct 堆栈中右侧的对齐方式

SwiftUI:如何为 ScrollView 制作可拉伸(灵活)的粘性标题?

用溢出的长文本对齐 Flutter 中的行和列

类型任何视图不能符合具有泛型的协议上的视图

从远程通知启动时,iOS 应用程序加载错误

构建失败:ld:重复符号 _OBJC_CLASS_$_Algebra5FirstViewController

在主线程上发布 NSNotification

UIView 和 initWithFrame 以及一个 NIB 文件.如何加载 NIB 文件?

检测 iOS UIDevice 方向

如何在键盘上方添加工具栏?

应用程序不包含正确的测试版权利

使用 swift 从应用程序委托打开视图控制器

什么是强属性属性

如何在 iOS 中获取正在运行的应用程序的名称

为给定的 UIColor 获取更亮和更暗的 colored颜色 变化