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
}
}
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).
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
Change the Value That Is Being Set in Variable's Willset Block
Navigationview Pops Back to Root, Omitting Intermediate View
Swift Write/Save/Move a Document File to Icloud Drive
Determine If Any.Type Is Optional
Swift Semantics Regarding Dictionary Access
Check Whether Swift Object Is an Instance of a Given Metatype
Array of Nested Type: Why Does the Compiler Complain
How to "Strongify" Optional Self Using Guard in Swift 2.0
Swift Equivalent to _Attribute((Objc_Requires_Super))
Remove Element from Collection During Iteration with Foreach
Fixing Nsurlconnection Deprecation from Swift 1.2 to 2.0
Init(Start:End:)' Is Deprecated: It Will Be Removed in Swift 3. Use the '..<' Operator
How to Replicate Promisekit-Style Chained Async Flow Using Combine + Swift
Arkit: Placing an Scntext at a Particular Point in Front of the Camera
Xcode 9 and Xcode 10 Giving Different Results, Even with Same Swift Version
Trying to Know When a Window Closes in a MACos Document Based Application