SwiftUI – 将数据从 SwiftUIView 传递到 SceneKit [英] SwiftUI – Passing data from SwiftUIView to SceneKit

查看:50
本文介绍了SwiftUI – 将数据从 SwiftUIView 传递到 SceneKit的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Goal: SwiftUI toggle button that controls a SwiftUI View with Scenekit (UIViewRepresentable)

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

// show statistics such as fps and timing information

我做了什么:这是 UIViewRepresentable ScenekitView

What I did: This is the 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的菜单

This is the Menu to control 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 顶部有菜单:

And this is Main Scene, with Menu on top of Scenekit:

import SwiftUI

struct MainView: View {
    var body: some View {

        ZStack {
        ScenekitView()
        Menu()
        }


    }
}

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

Question: How can I pass data from ScenekitView to Menu, so I can toggle and show/hide stats?

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

I tried ObservableObject but could not make it work. Also looked into other SO threads, but none worked for me.

非常感谢任何帮助!

为大家干杯

推荐答案

更新:2020 年 3 月 11 日.

Updated: March 11, 2020.

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

To pass data to updateNSView(_:context:) or updateUIView(_:context:) instance method you need to create @State and @Binding properties.

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

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

I've written this code for macOS but you can easily change it for 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 属性切换对象并以 60 fps 更新 renderer() 方法(例如).

Additionally you can use Coordinator object and delegate to this coordinator for switching objects using control property and for updating anything at 60 fps inside renderer() method (for example).

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

In this case you do not need to use updateNSView() or updateUIView() method:

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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆