我们有一个 ViewController,其中包含一个节点 sphereNode,其中包含我们的球体。
要旋转球体,我们可以使用UIPanGestureRecognizer。
由于识别器报告了我们的手指在屏幕上移动的总距离,我们缓存了报告给我们的最后一个点。
var previousPanPoint: CGPoint?
let pixelToAngleConstant: Float = .pi / 180
func handlePan(_ newPoint: CGPoint) {
if let previousPoint = previousPanPoint {
let dx = Float(newPoint.x - previousPoint.x)
let dy = Float(newPoint.y - previousPoint.y)
rotateUp(by: dy * pixelToAngleConstant)
rotateRight(by: dx * pixelToAngleConstant)
}
previousPanPoint = newPoint
}
我们计算dx 和dy 自上次调用识别器以来手指在每个方向上移动了多少像素。
使用pixelToAngleConstant,我们将像素值转换为角度(以弧度为单位)来旋转我们的球体。使用更大的常数以获得更快的旋转。
手势识别器返回一个state,我们可以使用它来确定手势是开始、结束还是手指已经移动。
当手势开始时,我们将手指位置保存在previousPanPoint 中。
当我们的手指移动时,我们调用上面的函数。
当手势结束或取消时,我们清除我们的previousPanPoint。
@objc func handleGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
switch gestureRecognizer.state {
case .began:
previousPanPoint = gestureRecognizer.location(in: view)
case .changed:
handlePan(gestureRecognizer.location(in: view))
default:
previousPanPoint = nil
}
}
我们如何旋转我们的球体?
函数rotateUp 和rotateRight 只是调用我们更通用的函数rotate(by: around:),它不仅接受角度,还接受要旋转的轴。
rotateUp 绕 x 轴旋转,rotateRight 绕 y 轴旋转。
func rotateUp(by angle: Float) {
let axis = SCNVector3(1, 0, 0) // x-axis
rotate(by: angle, around: axis)
}
func rotateRight(by angle: Float) {
let axis = SCNVector3(0, 1, 0) // y-axis
rotate(by: angle, around: axis)
}
rotate(by:around:) 在这种情况下相对简单,因为我们假设节点没有平移/我们想围绕节点局部坐标系的原点旋转。
当我们看一个一般情况时,一切都会稍微复杂一些,但这个答案只是一个很小的起点。
func rotate(by angle: Float, around axis: SCNVector3) {
let transform = SCNMatrix4MakeRotation(angle, axis.x, axis.y, axis.z)
sphereNode.transform = SCNMatrix4Mult(sphereNode.transform, transform)
}
我们从angle 和axis 创建一个旋转矩阵,并将我们球体的旧transform 乘以计算得到的新transform。
这是我创建的小演示:
这种方法有两个主要缺点。
它只围绕节点坐标原点旋转,只有在节点位置为SCNVector3Zero时才能正常工作
它既不考虑手势的速度,也不考虑手势停止时球体继续旋转。
使用这种方法无法轻松实现类似于表格视图的效果,您可以在其中翻转手指并且表格视图快速滚动然后放慢速度。
一种解决方案是为此使用物理系统。