如何使用 SwiftUI 更新 SceneKit 场景?
How to update a SceneKit scene with SwiftUI?
我在 SceneKit 的 SCNScene
中有一个圆柱体作为 SCNCylinder
,我想在 SwiftUI 的框架中显示它。我的目标是将圆柱体旋转 180° 或 90°(由用户选择)。为了获取(旋转角度的)输入,我在 SwiftUI 中使用了 Text()
和 onTapGesture{ .... }
属性。点击文本后,圆柱体会旋转,但现在我有两个圆柱体,一个在原始位置,另一个以所需角度旋转。我不确定为什么会这样。我想要同一个圆柱体旋转,而不是一个完全相同的圆柱体。我已经使用 @State
和 @Binding
连接了 SwiftUI 视图和 SceneKit 视图。
这是我的代码:
struct ContentView: View {
@State var rotationAngle = 0
var body: some View {
VStack{
Text("180°").onTapGesture {
self.rotationAngle = 180
}
Spacer()
Text("90°").onTapGesture {
self.rotationAngle = 90
}
SceneKitView(angle: $rotationAngle)
.position(x: 225.0, y: 200)
.frame(width: 300, height: 300, alignment: .center)
}
}
}
struct SceneKitView: UIViewRepresentable {
@Binding var angle: Int
func degreesToRadians(_ degrees: Float) -> CGFloat {
return CGFloat(degrees * .pi / 180)
}
func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
return sceneView
}
func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
cylinder.firstMaterial?.diffuse.contents = UIColor.green
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
let inttofloat = Float(self.angle)
let rotation = SCNAction.rotate(by: self.degreesToRadians(inttofloat), around: SCNVector3(1, 0, 0), duration: 5)
cylindernode.runAction(rotation)
sceneView.scene?.rootNode.addChildNode(cylindernode)
}
typealias UIViewType = SCNView
}
我想要一个给定角度的圆柱体旋转。
问题是,updateUIView 会被调用多次。您可以通过在此处添加调试点来检查这一点。因此,您的气缸将被添加多次。所以你可以通过多种方式解决这个问题......一种方法是在开始动画之前删除场景视图中的所有节点,如下所示:
func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
sceneView.scene?.rootNode.enumerateChildNodes { (node, stop) in
node.removeFromParentNode()
}
我在 SceneKit 的 SCNScene
中有一个圆柱体作为 SCNCylinder
,我想在 SwiftUI 的框架中显示它。我的目标是将圆柱体旋转 180° 或 90°(由用户选择)。为了获取(旋转角度的)输入,我在 SwiftUI 中使用了 Text()
和 onTapGesture{ .... }
属性。点击文本后,圆柱体会旋转,但现在我有两个圆柱体,一个在原始位置,另一个以所需角度旋转。我不确定为什么会这样。我想要同一个圆柱体旋转,而不是一个完全相同的圆柱体。我已经使用 @State
和 @Binding
连接了 SwiftUI 视图和 SceneKit 视图。
这是我的代码:
struct ContentView: View {
@State var rotationAngle = 0
var body: some View {
VStack{
Text("180°").onTapGesture {
self.rotationAngle = 180
}
Spacer()
Text("90°").onTapGesture {
self.rotationAngle = 90
}
SceneKitView(angle: $rotationAngle)
.position(x: 225.0, y: 200)
.frame(width: 300, height: 300, alignment: .center)
}
}
}
struct SceneKitView: UIViewRepresentable {
@Binding var angle: Int
func degreesToRadians(_ degrees: Float) -> CGFloat {
return CGFloat(degrees * .pi / 180)
}
func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
return sceneView
}
func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
cylinder.firstMaterial?.diffuse.contents = UIColor.green
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
let inttofloat = Float(self.angle)
let rotation = SCNAction.rotate(by: self.degreesToRadians(inttofloat), around: SCNVector3(1, 0, 0), duration: 5)
cylindernode.runAction(rotation)
sceneView.scene?.rootNode.addChildNode(cylindernode)
}
typealias UIViewType = SCNView
}
我想要一个给定角度的圆柱体旋转。
问题是,updateUIView 会被调用多次。您可以通过在此处添加调试点来检查这一点。因此,您的气缸将被添加多次。所以你可以通过多种方式解决这个问题......一种方法是在开始动画之前删除场景视图中的所有节点,如下所示:
func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
sceneView.scene?.rootNode.enumerateChildNodes { (node, stop) in
node.removeFromParentNode()
}