Swiftui - Passing Data from Swiftuiview to Scenekit

SwiftUI – Passing data from SwiftUIView to SceneKit

Updated: March 11, 2020.

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

Use the following code to find out how to do it:

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
}
}

Sample Image

Sample Image

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).

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

control.sceneView.backgroundColor = NSColor(
red: CGFloat(arc4random_uniform(256)) / 255.0,
green: CGFloat(arc4random_uniform(256)) / 255.0,
blue: CGFloat(arc4random_uniform(256)) / 255.0,
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) { }
}

How do I pass data from viewController to swiftUI view?

You want datos to be passed to the constructor of the View, but the only datos you have in your code is a local variable in a closure.

You need to make a new property in the VC

var datos: [[String]]?

Then, when you get the data, save it in this property

self.datos = ....

Then, when you construct the View, pass self.datos

SwiftUI - how to add a Scenekit Scene

You don't need use UIViewRepresentable anymore. Here's an update code for SwiftUI

import SwiftUI
import SceneKit

struct ContentView: View {
var scene: SCNScene? {
SCNScene(named: "Models.scnassets/Avatar.scn")
}

var cameraNode: SCNNode? {
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 2)
return cameraNode
}

var body: some View {
SceneView(
scene: scene,
pointOfView: cameraNode,
options: [
.allowsCameraControl,
.autoenablesDefaultLighting,
.temporalAntialiasingEnabled
]
)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

How to access SCNSceneRendererDelegate methods when using SceneKit with SwiftUI?

Found the solution in this answer:

SwiftUI – Passing data from SwiftUIView to SceneKit

At the lower half of Andy's question he describes how to use a coordinator to implement the delegate methods. Reproducing here for convenience:

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) { }
}

Is it possible to use SwiftUI for UI in an AR-App using SceneKit?

In a certain way you can. Here's a Medium story on this topic. Also this SO post might be useful.

How to use SwiftUI and SceneKit together?

try this: But because of the lot of questions you ask and they are so basic, maybe you should check for some beginner swift courses to learn how to set variables...and this has nothing to do with Scenekit and SwiftUI - it is just changing a variable.

struct ContentView: View {
@State var rotationAngle: Angle = .degrees(0)
var body: some View {

VStack{

Text("180º").onTapGesture {
self.rotationAngle += .degrees(90)
print(self.rotationAngle)
}
SceneKitView(radius: 0.02, height: 2, angle: $rotationAngle)
.position(x: 200.0, y: 140)
.frame(width: 300, height: 300, alignment: .center)
}
}
}


Related Topics



Leave a reply



Submit