SwiftUI –将数据从SwiftUIView传递到SceneKit [英] SwiftUI – Passing data from SwiftUIView to 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()
}
}
这是Main Scene,在Scenekit顶部有Menu:
And this is Main Scene, with Menu on top of Scenekit:
import SwiftUI
struct MainView: View {
var body: some View {
ZStack {
ScenekitView()
Menu()
}
}
}
问题:如何将数据从ScenekitView传递到菜单,以便可以切换和显示/隐藏统计信息?
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
属性.
使用以下代码来查找操作方法:
我已经为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
属性切换对象并在renderer()
方法内以60 fps更新任何内容.(例如).
Additionally you can use
Coordinator
object anddelegate
to this coordinator for switching objects usingcontrol
property and for updating anything at 60 fps insiderenderer()
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屋!