如何用SwiftUI手势控制非AR模式ARView的相机位置?
在非AR模式下用SwiftUI手势控制ARView相机位置的实现方案
完全可行,核心思路是直接操作ARView底层SceneKit场景的相机节点,结合SwiftUI手势将触摸操作转化为相机的位置调整。以下是具体实现步骤和代码示例:
关键原理
非AR模式下的ARView本质是包裹了一个SceneKit场景,你可以通过arView.scene获取该场景的引用,并找到其中的相机节点自行控制——这相当于手动实现SceneKit的allowsCameraControl功能。
实现步骤
1. 封装ARView并传递引用
先实现UIViewRepresentable,将ARView的实例传递给SwiftUI父视图,方便后续操作相机节点:
import SwiftUI import ARKit struct ARViewContainer: UIViewRepresentable { @Binding var arView: ARView? func makeUIView(context: Context) -> ARView { // 初始化非AR模式的ARView let arView = ARView(frame: .zero, cameraMode: .nonAR, automaticallyConfigureSession: false) // 加载USDZ模型并添加到场景 if let modelURL = Bundle.main.url(forResource: "YourModel", withExtension: "usdz") { let modelEntity = try! Entity.load(contentsOf: modelURL) // 将模型放在相机前方合适位置 modelEntity.position = SIMD3(x: 0, y: 0, z: -2) arView.scene.addAnchor(modelEntity) } self.arView = arView return arView } func updateUIView(_ uiView: ARView, context: Context) {} }
2. 添加SwiftUI手势控制相机
在SwiftUI主视图中,通过DragGesture控制相机的XY轴平移,PinchGesture控制相机的Z轴远近:
struct ContentView: View { @State private var arView: ARView? @State private var lastDragPoint: CGPoint? @State private var baseCameraZ: Float = -2 // 记录初始相机Z位置 var body: some View { ZStack { ARViewContainer(arView: $arView) // 拖拽手势:控制相机左右/上下移动 .gesture( DragGesture() .onChanged { gesture in guard let cameraNode = arView?.scene.rootNode.childNodes.first(where: { $0.camera != nil }) else { return } if let lastPoint = lastDragPoint { let deltaX = gesture.location.x - lastPoint.x let deltaY = gesture.location.y - lastPoint.y // 调整灵敏度系数,适配不同屏幕尺寸 let sensitivity: Float = 0.01 cameraNode.position.x -= Float(deltaX) * sensitivity cameraNode.position.y += Float(deltaY) * sensitivity } lastDragPoint = gesture.location } .onEnded { _ in lastDragPoint = nil } ) // 捏合手势:控制相机远近 .gesture( PinchGesture() .onChanged { gesture in guard let cameraNode = arView?.scene.rootNode.childNodes.first(where: { $0.camera != nil }) else { return } // 根据捏合比例调整相机Z位置,限制范围避免异常 let newZ = baseCameraZ * Float(1 / gesture.scale) cameraNode.position.z = max(-10, min(-0.5, newZ)) } .onEnded { _ in // 更新基准Z位置,为下次捏合做准备 baseCameraZ = arView?.scene.rootNode.childNodes.first(where: { $0.camera != nil })?.position.z ?? -2 } ) } } }
注意事项
- 相机节点查找:非AR模式下ARView会自动创建SCNCamera节点,通过
rootNode.childNodes.first(where: { $0.camera != nil })即可获取。 - 坐标系适配:SceneKit的坐标系中,Z轴值越小(更负)相机越远,Z轴值越大(接近0)相机越近;X轴向右为正,Y轴向上为正,拖拽时的偏移方向需要对应调整。
- 手感优化:根据需求调整灵敏度系数和相机位置限制范围,让手势操作更符合直觉。
内容的提问来源于stack exchange,提问作者Scott McKenzie
相关产品推荐
相关产品推荐

