第二次启动控制器时,会自动调用 ViewModel 类中的 .sink 块

问题描述 投票:0回答:1

我使用 UIHostingController 将 SwiftUI 视图嵌入到控制器 A 中。控制器 A 从控制器 B 启动。SwiftUI 视图使用 ViewModel 类。在ViewModel中,我订阅了一些发布者,我想将数据发送到控制器A。

//This is the ViewModel file that contains a publisher to receive the video URL upon capture, and I am sending this URL to a controller using PassthroughSubject.

class GenericCameraViewViewModel: ObservableObject {
    let xCamSession: XCamSession
    
    var preview: some View {
        xCamSession.interactivePreview()
    }
    
    private var subscription = Set<AnyCancellable>()
    
    public var cameraEventSubject = PassthroughSubject<GenericCameraResult, Never>()
    
    
    init(genericCameraConfiguration: GenericCameraConfiguation) {

        // Prepare video album cover
        xCamSession.videoFilePublisher
            .receive(on: DispatchQueue.main)
            .map {result -> URL? in
                if case .success(let file) = result {
                    return file.path
                } else {
                    return nil
                }
            }
            .sink(receiveValue: { fileURL in
                debugPrint("Receive file url")
                self.cameraEventSubject.send(GenericCameraResult(event: .capturedVideo, images: nil, videoFile: fileURL))
//                if self.shouldSendData {
//                    self.xCamSession.terminateSession({ result in
//                        self.cameraEventSubject.send(GenericCameraResult(event: .capturedVideo, images: nil, videoFile: fileURL))
//                        debugPrint("Receive file url")
//                        
//                    })
//                }
                
            })
            .store(in: &subscription)
        
        }
}
// a function in the controller to add SwiftUI view and subscribe the subject
    
    // display the measure view as a view controller child
    private func addAsAChild(){
        
        //let config = GenericCameraConfiguation(captureMode: .photo,canCaputreMultiplePhotos: true, cameraPosition: .back,cameraPhotoFlash: .off,cameraVideoTorch: .auto)
        
        let viewModel = GenericCameraViewViewModel(genericCameraConfiguration: configuration)
        let videoContentView = GenericCameraView(viewModel: viewModel)
        viewModel.cameraEventSubject
        .receive(on: DispatchQueue.main) // Ensure sink runs on main thread
        .sink(
            receiveCompletion: { completion in
                print("-- completion", completion)
                self.cancellables.first?.cancel()
                self.dismiss(animated: true)
                
            },
            receiveValue: {[weak self] result in
                viewModel.shouldSendData = false
                switch(result.event){
                case .closePressed:
                    viewModel.cameraEventSubject.send(completion: .finished)
                    debugPrint("Close Button Pressed")
                    break
                case .donePressed:
                    debugPrint("Done Button Pressed  \(result.images ?? [])")
                    viewModel.cameraEventSubject.send(completion: .finished)
                    let capturedImages = result.images ?? []
                    let arrImagePaths = self?.saveImages(images: capturedImages)
                    self?.mDelegate?.capturedImagesPath(capturedImages: arrImagePaths ?? [])
                    break;
                case .capturedPhoto:
                    debugPrint("capturedPhoto Button Pressed  \(result.images ?? [])")
                    let capturedImages = result.images ?? []
                    let arrImagePaths = self?.saveImages(images: capturedImages)
                    self?.mDelegate?.capturedImagesPath(capturedImages: arrImagePaths ?? [])
                    
                case .capturedVideo:
                    debugPrint("capturedVideo Button Pressed  \(result.videoFile ??  URL.init(string: "https://cameraxaapp.com")!)")
                    self?.mDelegate?.capturedVideoUrl(videoUrl: result.videoFile)
                    //viewModel.cameraEventSubject.send(completion: .finished)
                }
            }
        ).store(in: &cancellables)
        
        let hostingViewController = HostingController(rootView: videoContentView)
        addChildWithView(hostingViewController)
    }

为了实现这一目标,我在 ViewModel 中添加了一个 PassthroughSubject 并在控制器 A 中订阅了该主题。我在发布者的 .sink 方法中使用该主题发送数据,并且工作正常。但是,当我再次打开控制器 A 时,会自动调用发布者的 .sink 方法。

swiftui combine
1个回答
0
投票

SwiftUI 不适用于视图模型对象,因为

View
结构已经是视图模型。您最好为回调事件提供闭包
VideoContentView
,例如

videoContentView.donePressed = { 
    finished()
}
struct VideoContentView: View {
    var donePressed: (() -> Void)?
...
© www.soinside.com 2019 - 2024. All rights reserved.