You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 16:23:09