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

如何在UIViewRepresentable实例中获取ARView实例?

在SwiftUI中获取ARView实例并捕获AR帧的正确方式

问题场景

在SwiftUI中集成ARKit的ARView时,标准做法是通过UIViewRepresentable结构体封装原生ARView:

struct ContentView : View {
   var body: some View {
      ARViewContainer().edgesIgnoringSafeArea(.all)
   }
}

struct ARViewContainer: UIViewRepresentable {
    func makeUIView(context: Context) -> ARView {
        let arView = ARView(frame: .zero)
        // 配置ARView(如开启追踪)
        return arView
    }
    func updateUIView(_ uiView: ARView, context: Context) {}
}

但直接在ARViewContainer结构体中声明可变属性存储ARView会报错——因为Swift结构体是值类型,默认不可变:

struct ARViewContainer: UIViewRepresentable {
    var arView : ARView!  // 错误:结构体不可变
    func makeUIView(context: Context) -> ARView {
        arView = ARView(frame: .zero)
        // 配置ARView
        return arView
    }
    func updateUIView(_ uiView: ARView, context: Context) {}
}

提前初始化ARView虽然能编译,但破坏了封装性和复用性,代码存在不良气味:

struct ARViewContainer: UIViewRepresentable {
    let arView = ARView(frame: .zero)
    func makeUIView(context: Context) -> ARView {
        // 配置ARView
        return arView
    }
    func updateUIView(_ uiView: ARView, context: Context) {}
}

我们需要一种合理的方式获取makeUIView创建的ARView实例,从而实现点击屏幕或按钮时捕获AR帧:

let currentFrame = arView.session.currentFrame

解决方案

方案1:使用UIViewRepresentable的Coordinator

Coordinator是UIViewRepresentable的配套管理对象,专门用于处理原生视图的生命周期、交互逻辑,我们可以在Coordinator中持有ARView的弱引用:

class ARViewCoordinator: NSObject {
    weak var arView: ARView?
    
    // 封装捕获帧的方法
    func captureCurrentFrame() -> ARFrame? {
        return arView?.session.currentFrame
    }
}

struct ARViewContainer: UIViewRepresentable {
    // 自定义Coordinator
    func makeCoordinator() -> ARViewCoordinator {
        ARViewCoordinator()
    }
    
    func makeUIView(context: Context) -> ARView {
        let arView = ARView(frame: .zero)
        // 将ARView绑定到Coordinator
        context.coordinator.arView = arView
        // 配置ARView
        let config = ARWorldTrackingConfiguration()
        arView.session.run(config)
        return arView
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
}

在ContentView中,通过@StateObject持有Coordinator,确保生命周期与视图一致:

struct ContentView: View {
    @StateObject private var coordinator = ARViewCoordinator()
    
    var body: some View {
        ZStack {
            ARViewContainer()
                .edgesIgnoringSafeArea(.all)
            
            Button("捕获AR帧") {
                if let frame = coordinator.captureCurrentFrame() {
                    // 处理捕获到的帧
                    print("捕获到AR帧,时间戳:\(frame.timestamp)")
                }
            }
            .foregroundColor(.white)
            .padding()
            .background(Color.black.opacity(0.7))
            .cornerRadius(8)
        }
    }
}

方案2:使用ObservableObject管理ARView

创建一个ObservableObject类来统一管理ARView实例和相关逻辑,符合SwiftUI的状态管理模式:

class ARViewModel: ObservableObject {
    private(set) var arView: ARView?
    
    // 捕获AR帧的方法
    func captureFrame() -> ARFrame? {
        arView?.session.currentFrame
    }
    
    // 初始化并配置ARView
    func setupARView() -> ARView {
        let arView = ARView(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        arView.session.run(config)
        self.arView = arView
        return arView
    }
}

struct ARViewContainer: UIViewRepresentable {
    let viewModel: ARViewModel
    
    func makeUIView(context: Context) -> ARView {
        viewModel.setupARView()
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
}

struct ContentView: View {
    @StateObject private var viewModel = ARViewModel()
    
    var body: some View {
        ZStack {
            ARViewContainer(viewModel: viewModel)
                .edgesIgnoringSafeArea(.all)
            
            Button("捕获帧") {
                if let frame = viewModel.captureFrame() {
                    print("帧追踪状态:\(frame.camera.trackingState)")
                }
            }
            .foregroundColor(.white)
            .padding()
            .background(Color.blue)
            .cornerRadius(8)
        }
    }
}

方案3:单例模式(仅适用于全局唯一ARView场景)

如果你的应用确实只需要一个ARView实例,可以使用单例模式,但要注意内存管理和复用性:

class ARViewManager {
    static let shared = ARViewManager()
    private(set) var arView: ARView?
    
    // 私有构造方法,确保单例唯一性
    private init() {}
    
    func setupARView() -> ARView {
        if let existingView = arView {
            return existingView
        }
        let arView = ARView(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        arView.session.run(config)
        self.arView = arView
        return arView
    }
    
    func captureCurrentFrame() -> ARFrame? {
        arView?.session.currentFrame
    }
}

struct ARViewContainer: UIViewRepresentable {
    func makeUIView(context: Context) -> ARView {
        ARViewManager.shared.setupARView()
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
}

struct ContentView: View {
    var body: some View {
        ZStack {
            ARViewContainer()
                .edgesIgnoringSafeArea(.all)
            
            Button("捕获AR帧") {
                if let frame = ARViewManager.shared.captureCurrentFrame() {
                    print("捕获到帧:\(frame)")
                }
            }
            .foregroundColor(.white)
            .padding()
            .background(Color.black.opacity(0.7))
            .cornerRadius(8)
        }
    }
}

注意事项

  • 优先选择Coordinator或ObservableObject方案,它们更符合SwiftUI的设计理念,代码耦合度更低,可维护性更强
  • 单例模式仅适合全局唯一ARView的场景,否则会导致视图复用问题
  • 持有ARView时建议使用weak引用(如方案1中的Coordinator),避免循环引用导致内存泄漏

内容的提问来源于stack exchange,提问作者wcochran

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:47:20