SwiftUI – 将数据从 SwiftUIView 传递到 SceneKit

Posted

技术标签:

【中文标题】SwiftUI – 将数据从 SwiftUIView 传递到 SceneKit【英文标题】:SwiftUI – Passing data from SwiftUIView to SceneKit 【发布时间】:2020-06-20 06:30:07 【问题描述】:

目标: 使用 Scenekit (UIViewRepresentable) 控制 SwiftUI 视图的 SwiftUI 切换按钮

//显示fps和计时信息等统计信息

我做了什么: 这是 UIViewRepresentable ScenekitView

import SwiftUI
import SceneKit

struct ScenekitView : UIViewRepresentable 
    let scene = SCNScene(named: "art.scnassets/ship.scn")!

    func makeUIView(context: Context) -> SCNView 
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)

        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)

        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)

        // retrieve the ship node
        let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!

        // animate the 3d object
        ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))

        // retrieve the SCNView
        let scnView = SCNView()
        return scnView
    

    func updateUIView(_ scnView: SCNView, context: Context) 
        scnView.scene = scene

        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true

        // show statistics such as fps and timing information

        scnView.showsStatistics = true

        // configure the view
        scnView.backgroundColor = UIColor.black

 


struct ScenekitView_Previews: PreviewProvider 
    static var previews: some View 
        ScenekitView()
    

这是控制 Scenekit 的菜单

import SwiftUI


struct Menu: View 

 @State var showstats = false


    var body: some View 

        HStack 

        Form 
            Toggle(isOn:) 
               Text("Stats")
            
        
        .frame(width: 200.0)

            Spacer()
    
  
 
struct Menu_Previews: PreviewProvider 
    static var previews: some View 
        Menu()
    

这是主场景,菜单位于 Scenekit 之上:

import SwiftUI

struct MainView: View 
    var body: some View 

        ZStack 
        ScenekitView()
        Menu()
        


    

问题:如何将数据从 ScenekitView 传递到菜单,以便切换和显示/隐藏统计信息?

我尝试了 ObservableObject 但无法使其工作。还查看了其他 SO 线程,但没有一个对我有用。

非常感谢任何帮助!

向大家干杯

【问题讨论】:

您正在寻找的是一个协调器,如 Apples SwiftUI 教程中所示:developer.apple.com/tutorials/swiftui/interfacing-with-uikit 您不应该,而是创建视图模型(作为 ObservableObject)并在菜单和场景中传递它,因此从任何位置修改该视图模型两个视图都可以做出相应的反应。 【参考方案1】:

更新2020 年 3 月 11 日

要将数据传递给 updateNSView(_:context:)updateUIView(_:context:) 实例方法,您需要创建 @State@Binding 属性。

使用以下代码了解如何操作

我已经为 macOS 编写了这段代码,但您可以轻松地为 ios 更改它:

import SwiftUI
import SceneKit

struct ContentView: View 

    @State var stats: Bool = true

    var body: some View 
        HStack 
            ScenekitView(showStats: $stats)
            VStack 
                Button(action: 
                    self.stats.toggle()
                , label: 
                    Text(self.stats ? "ON" : "OFF")
                )
            
            .frame(width: 150.0)
        
    


struct ScenekitView: NSViewRepresentable 

    @Binding var showStats: Bool
    let sceneView = SCNView(frame: .zero)
    let scene = SCNScene(named: "art.scnassets/ship.scn")!

    func scnScene(stat: Bool, context: Context) -> SCNView 
        sceneView.scene = scene
        sceneView.showsStatistics = stat
        return sceneView
    

    func makeNSView(context: Context) -> SCNView 
        scnScene(stat: true, context: context)
    

    func updateNSView(_ uiView: SCNView, context: Context) 
        uiView.allowsCameraControl = true
        uiView.backgroundColor = NSColor.black
        uiView.showsStatistics = showStats        
    

此外,您还可以使用 Coordinator 对象和 delegate 到此协调器,以使用 control 属性切换对象,并在 renderer() 方法内以 60 fps 更新任何内容(例如)。

在这种情况下您不需要使用updateNSView()updateUIView() 方法:

struct ScenekitView: NSViewRepresentable 

    @Binding var showStats: Bool
    let sceneView = SCNView(frame: .zero)
    let scene = SCNScene(named: "art.scnassets/ship.scn")!

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

    final class Coordinator: NSObject, SCNSceneRendererDelegate 
        var control: ScenekitView

        init(_ control: ScenekitView) 
            self.control = control
        

        func renderer(_ renderer: SCNSceneRenderer,
               updateAtTime time: TimeInterval) 

            control.sceneView.showsStatistics = control.showStats

            for i in 0...255 
                control.sceneView.backgroundColor = NSColor(
                                  red: CGFloat(arc4random_uniform(UInt32(i))),
                                green: CGFloat(arc4random_uniform(UInt32(i))),
                                 blue: CGFloat(arc4random_uniform(UInt32(i))),
                                alpha: 1.0)
            
        
    

    func scnScene(stat: Bool, context: Context) -> SCNView 
        sceneView.scene = scene
        sceneView.showsStatistics = stat
        sceneView.delegate = context.coordinator
        return sceneView
    

    func makeNSView(context: Context) -> SCNView 
        scnScene(stat: true, context: context)
    

    func updateNSView(_ uiView: SCNView, context: Context)  

【讨论】:

以上是关于SwiftUI – 将数据从 SwiftUIView 传递到 SceneKit的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI – 将数据从 SwiftUIView 传递到 SceneKit

将核心数据从 Swift 迁移到 SwiftUI

将数据从委托 Swift 类传递到 SwiftUI 结构中的 EnvironmentObject

SwiftUI 将数据从 Firebase 加载到 TextView

无法将数据从 Firebase 附加到数组 SwiftUI

将数据从模型传递到 SwiftUI 中的视图