我将
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")
}
鉴于
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
之间的连接,允许直接修改变量,同时还可以防止您遇到的重新初始化问题。