我正在 SwiftUI 中使用
SceneView
加载 3D 对象:
SceneView(
scene: scene,
pointOfView: cameraNode,
options: [.autoenablesDefaultLighting]
)
我已从
.allowsCameraControl
中删除了 options
属性以手动处理手势。我希望能够水平旋转对象。在 UIKit 中,我可以使用 UIPanGestureRecognizer
来做到这一点,如下所示:
func rotateObject(_ gesture: UIPanGestureRecognizer) {
var currentAngleY: Float = 0.0
guard let nodeToRotate = scene?.rootNode else { return }
let translation = gesture.translation(in: gesture.view!)
var newAngleY = (Float)(translation.x) * (Float)(Double.pi) / 180.0
newAngleY += currentAngleY
nodeToRotate.eulerAngles.y = newAngleY
if(gesture.state == .ended) { currentAngleY = newAngleY }
}
但是在 SwiftUI 中我不确定如何做到这一点,任何帮助将不胜感激。
我刚刚弄清楚这一点,部分原因是我对无用的自我推销答案感到非常恼火。
基本上,您只需将 DragGesture 添加到 SceneView,然后使用拖动位置的某个因子,这意味着某个常数因子乘以位置增量(平移的宽度和高度)并将其转换为以弧度为单位的角度。在这里,我不知道距对象的视口距离,所以我只使用拖动移动点数的 1/100,将其用作度数,然后除以 2 * Pi 转换为弧度。
如果你想移动某个指定的距离,你可能可以计算出来,但通常我认为人们只想要一些拖动长度与角度的比率。
在手势更改期间,您希望将旋转重置为开始的位置,否则行为会有点奇怪(除非您越过起点,否则旋转不会改变方向)
最后,您希望将其保存为最后一次旋转,以便下次看到拖动时可以从那里开始。
在 SwiftUI(SwiftUI 4)中:
@State private var lastRotation: SCNVector4 = .init()
var body: some View {
VStack {
SceneView(scene: scene, preferredFramesPerSecond: 30 )
.gesture( DragGesture()
.onChanged({ dgv in
let locationXDelta = dgv.translation.width
let locationYDelta = dgv.translation.height
let dragSensitivityFactor = 0.01
let rX = locationXDelta * dragSensitivityFactor / .pi * 2
let rY = locationYDelta * dragSensitivityFactor / .pi * 2
scene.rootNode.rotation = lastRotation
self.scene.rootNode.simdLocalRotate(by: .init(angle: Float(rX), axis: .init(x: 0, y: 1, z: 0)))
self.scene.rootNode.simdLocalRotate(by: .init(angle: Float(rY), axis: .init(x: 1, y: 0, z: 0)))
})
.onEnded({ dgv in
self.lastRotation = scene.rootNode.rotation
}))
}
.padding()
}
您可以在这篇文章中找到解决方案背后的完整故事。 SwiftUI 中的代码。
Circle()
.fill(Color.white)
.opacity(0.1)
.frame(width: 256, height: 256, alignment: .center)
.gesture(
DragGesture(minimumDistance: 30, coordinateSpace: .global)
.onChanged { gesture in
startWalking.send(gesture.translation)
}
.onEnded { _ in
stopWalking.send(true)
}
)
您可以使用拖动手势和订阅在 sceneView 和 SwiftUI 界面之间传输信息。