从 SwiftUI 视图到视图控制器执行函数

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

我将

GlobeView()
嵌入到 SwiftUI 视图中。我想调用一个函数从 SwiftUI 视图执行到
GlobeViewController

考虑到分层视图层次结构,实现此目的最简单的方法是什么?
我知道我可以传递一个简单的闭包,但这样做时,我遇到了一个错误,其中

SCNParticleSystem
被无限地添加到
SceneView
中,因为每次视图更新时 init 都会触发。

有其他方法可以调用这个函数吗?
有没有办法检测

viewModel
GlobeViewController
之间的变量变化?
我知道我可以在
didset
上使用
viewModel
,但是我怎么知道
viewModel
的特定成员变量何时被设置?

import SwiftUI
import SceneKit
import Foundation
import SceneKit
import CoreImage
import MapKit

class GlobeViewModel: ObservableObject {
    @Published var option: Int = 2
    @Published var hideSearch: Bool = false
    @Published var executeFunction: Bool = false
}

struct MainProfile: View {
    @EnvironmentObject var viewModel: GlobeViewModel
    
    var body: some View {
        GlobeView() //call function from here
    }
}

typealias GenericControllerRepresentable = UIViewControllerRepresentable

@available(iOS 13.0, *)
private struct GlobeViewControllerRepresentable: GenericControllerRepresentable {
    @EnvironmentObject var viewModel: GlobeViewModel
    
    func makeUIViewController(context: Context) -> GlobeViewController {
        let globeController = GlobeViewController(earthRadius: 1.0, popRoot: viewModel)
        return globeController
    }
    
    func updateUIViewController(_ uiViewController: GlobeViewController, context: Context) { }
}


@available(iOS 13.0, *)
public struct GlobeView: View {
    
    public var body: some View {
        GlobeViewControllerRepresentable()
    }
}

public typealias GenericController = UIViewController
public typealias GenericColor = UIColor
public typealias GenericImage = UIImage

public class GlobeViewController: GenericController {
    var viewModel: GlobeViewModel
    public var earthNode: SCNNode!
    internal var sceneView : SCNView!
    private var cameraNode: SCNNode!
    
    init(popRoot: GlobeViewModel) {
        self.viewModel = popRoot
        super.init(nibName: nil, bundle: nil)
    }
    
    init(popRoot: GlobeViewModel) {
        self.viewModel = popRoot
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
ios swift swiftui model scnnode
1个回答
0
投票

鉴于

GlobeViewModel
是在应用程序基础上初始化的环境对象,并且您希望
GlobeViewController
能够直接引用它,您当前的方法应该有效:您已经将
GlobeViewModel
传递给
GlobeViewController
通过初始化器。

面临的挑战是从

GlobeViewController
执行
GlobeView
中的函数,而不会导致
SCNParticleSystem
的多次初始化等问题。

您可以尝试在

GlobeViewController
中实现一种机制来观察
GlobeViewModel
中的变化,并做出相应的反应。

+--------------------+         +--------------------------+
|                    |         |                          |
|   MainProfile      |         |    GlobeView             |
|   (SwiftUI View)   |         |    (SwiftUI View)        |
|   @ObservedObject  |         |   Uses EnvironmentObject |
|    viewModel       |         |    viewModel             |
|                    |         |                          |
+---------+----------+         +--------------+-----------+
          |                                   |
          | Pass viewModel                    | Pass viewModel
          |                                   |
          v                                   v
+---------+----------+         +-------------+------------+
|                    |         |                          |
|  GlobeViewModel    |---------|  GlobeViewController     |
| (ObservableObject) |         |  (UIKit ViewController)  |
|                    |         |                          |
+--------------------+         +--------------------------+

GlobeViewController
是:

public class GlobeViewController: GenericController {
    var viewModel: GlobeViewModel
    private var isParticleSystemInitialized = false

    // Existing initializer code

    override func viewDidLoad() {
        super.viewDidLoad()

        // Setup observer for executeFunction
        viewModel.$executeFunction.sink { [weak self] newValue in
            if newValue {
                self?.executeRequiredFunction()
            }
        }.store(in: &cancellables) // Assuming you have a collection of AnyCancellable
    }

    private func executeRequiredFunction() {
        // Check to avoid reinitialization
        if !isParticleSystemInitialized {
            // Your logic to initialize SCNParticleSystem
            isParticleSystemInitialized = true
        }
    }
}

GlobeViewController
使用Combine 的
GlobeViewModel
方法观察
sink
的变化。该设置允许
GlobeViewController
对视图模型中的更改做出反应。

isParticleSystemInitialized
标志确保
SCNParticleSystem
的初始化仅发生一次,从而防止出现多次初始化的问题。

由于

GlobeViewModel
是一个环境对象,因此您可以将其传递给
GlobeViewController
,而无需在
MainProfile
中重新初始化它。

看看这是否会保持

GlobeViewController
GlobeViewModel
之间的连接,允许直接修改变量,同时还可以防止您遇到的重新初始化问题。

© www.soinside.com 2019 - 2024. All rights reserved.