我在SCNCylinder
中有一个圆柱作为SCNView
。我想将圆柱体旋转多个给定角度。我正在使用SwiftUI提供所需的输入(旋转角度)。假设我输入为90°。目前,它可以轻松旋转90°,但是如果我再给它输入180°,它会回到其原始位置,然后旋转180°。我希望它从90度旋转后的位置旋转180度。
这是我的代码:
struct ContentView: View {
@State var rotationAngle: Float = 0
var body: some View {
VStack{
Text("180°").onTapGesture {
self.rotationAngle = 180.0
}
Divider()
Text("90°").onTapGesture {
self.rotationAngle = 90.0
}
SceneKitView(angle: $rotationAngle)
.position(x: 225.0, y: 175)
.frame(width: 300, height: 300, alignment: .center)
}
}
}
struct SceneKitView: UIViewRepresentable {
@Binding var angle: Float
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>) {
sceneView.scene?.rootNode.enumerateChildNodes { (node, stop) in
node.removeFromParentNode() }
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 rotation = SCNAction.rotate(by: self.degreesToRadians(self.angle),
around: SCNVector3(1, 0, 0), duration: 5)
cylindernode.runAction(rotation)
sceneView.scene?.rootNode.addChildNode(cylindernode)
}
无论我给它几个角度,我都希望它能正确旋转。
答案 0 :(得分:1)
要清楚一点,您希望每次按下按钮时圆柱体都从其当前位置继续旋转。不要使圆柱体固定在90或180的位置。
因为您的当前代码似乎已设置为执行第二件事(rotate:by
除外)。 rotationAngle保持为固定状态。确实,您应该向节点发送旋转命令,而不必将其旋转量保持在状态变量中。
但是,使用现在的代码:
在updateUIView
内部,坚持只对节点进行更新。现在,您正在拆除并在每次rotationAngle
更改时将整个节点层次结构重新创建到其原始位置。这可能就是为什么要重绘的原因。
而是在makeUIView
或SceneKitView
的初始化程序内进行设置,而在updateUIView
内进行设置,只需应用新的旋转方式即可。
第二个问题是,当您通过按{180}来设置rotationAngle
时,如果再次按下相同的按钮,则该值将再次设置为180。就SwiftUI而言,该值没有改变。而言,不需要调用updateUIView
。有几种方法可以使它更新,但是一种快速简便的方法可能只是旋转0,然后立即旋转所需的角度。
但是同样,您实际上不应该将该角度保留在状态变量中。最好只在rotateCylinder(angle: Angle)
中创建一个SceneKitView
函数,然后从按钮中调用它。
此外,为什么不使用新的Angle
类型呢?这是其中一些更改的代码。我认为您正在尝试这样做,但是我可能是错的。
import SwiftUI
import UIKit
import SceneKit
struct ContentView: View {
@State var rotationAngle: Angle = .zero
var body: some View {
VStack{
HStack {
Spacer()
Text("180°").onTapGesture {
self.rotationAngle = .zero
self.rotationAngle = .degrees(180)
}
Spacer()
Divider()
Spacer()
Text("90°").onTapGesture {
self.rotationAngle = .zero
self.rotationAngle = .degrees(90)
}
Spacer()
}.frame(height: 100).padding()
SceneKitView(radius: 0.2, height: 2, angle: $rotationAngle)
}
}
}
struct SceneKitView: UIViewRepresentable {
@Binding var angle: Angle
let node: SCNNode
init(radius: CGFloat, height: CGFloat, angle: Binding<Angle>) {
let cylinder = SCNCylinder(radius: radius, height: height)
cylinder.firstMaterial?.diffuse.contents = UIColor.green
self.node = SCNNode(geometry: cylinder)
self._angle = angle
}
func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.autoenablesDefaultLighting = true
sceneView.scene?.rootNode.addChildNode(node)
return sceneView
}
func updateUIView(_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
print("Updating view \(angle.degrees)")
// To continue rotating
let rotation = SCNAction.rotate(by: CGFloat(angle.radians), around: SCNVector3(1, 0, 0), duration: 3)
// To go to a fixed angle state
//let rotation = SCNAction.rotate(toAxisAngle: SCNVector4(1, 0, 0, angle.radians), duration: 3)
node.runAction(rotation)
}
}