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

SwiftUI中ARGeoTrackingStatus与相机跟踪状态无法显示问题

问题排查结果

状态无法正常渲染是以下几个核心问题导致的:

  • 未绑定ARSession代理:代码启动ARSession后,从未将arView.session.delegate设置为Coordinator实例,所有ARSessionDelegate的状态回调根本不会触发,拿不到任何状态更新数据。
  • 跨线程更新UI违规:ARKit的代理回调、地理可用性检查回调默认都在后台子线程执行,直接在子线程修改@Binding绑定的状态属性,无法触发SwiftUI的UI刷新机制,还可能触发运行时线程安全警告。
  • 实例生命周期异常:将ARView实例作为UIViewRepresentable结构体的存储属性,SwiftUI每次重绘视图时都会重新创建ARView实例,导致AR会话中断、上下文不一致。
  • 状态逻辑遗漏:地理跟踪进入.localized成功状态时,代码仅更新了精度字段,没有更新geoTrackingStatus的文本,用户无法看到定位成功的状态。
  • 日志逻辑错误:设备不支持ARGeoTracking的分支中,错误打印了“支持地理跟踪”的日志,会误导问题排查。
修复方案
  1. 将ARView实例的创建移动到makeUIView方法内,避免结构体重建导致的实例异常,同时在创建ARView后立即设置session代理为Coordinator。
  2. 给Coordinator添加arView属性持有ARView引用,供引导视图添加、session重置使用。
  3. 所有修改viewModel状态的逻辑,必须用DispatchQueue.main.async包裹,保证在主线程更新UI状态。
  4. 补全.localized状态的文本赋值,修正错误的支持性判断日志。
修正后核心代码

ARViewContainer 部分

struct ARViewContainer: UIViewRepresentable {
    
    struct ViewModel {
        var notAvailable = false
        var geoTrackingStatus = ""
        var cameraTrackingStatus = ""
        var accuracy = ""
    }
    
    @Binding var viewModel: ViewModel
    
    func makeUIView(context: Context) -> ARView {
        // 在此处创建ARView实例,保证生命周期正确
        let arView = ARView(frame: .zero)
        // 核心:绑定session代理
        arView.session.delegate = context.coordinator
        // 给coordinator传递arView引用
        context.coordinator.arView = arView
        
        if ARGeoTrackingConfiguration.isSupported {
            ARGeoTrackingConfiguration.checkAvailability { available, error in
                // 切主线程更新状态
                DispatchQueue.main.async {
                    if !available {
                        viewModel.notAvailable = true
                        print("GeoTracking is not available at your location")
                    }
                }
            }
        } else {
            // 修正错误日志
            print("Your Device does not support ARGeoTracking")
        }
        
        let config = ARGeoTrackingConfiguration()
        arView.automaticallyConfigureSession = false
        arView.session.run(config)
        context.coordinator.addCoaching()
        return arView
    }
    
    func makeCoordinator() -> Coordiantor {
        Coordiantor(self)
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
        
    }
    
}

Coordinator 部分

class Coordiantor: NSObject, ARSessionDelegate, ARCoachingOverlayViewDelegate {
    
    var parent: ARViewContainer
    // 新增arView引用
    var arView: ARView?
    
    init(_ parent: ARViewContainer) {
        self.parent = parent
    }
    
    func addCoaching() {
        guard let arView = arView else { return }
        let coachingOverlay = ARCoachingOverlayView()
        coachingOverlay.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        coachingOverlay.goal = .geoTracking
        coachingOverlay.session = arView.session
        coachingOverlay.delegate = self
        arView.addSubview(coachingOverlay)
    }
    
    public func coachingOverlayViewDidRequestSessionReset(_ coachingOverlayView: ARCoachingOverlayView) {
        guard let arView = arView else { return }
        let config = ARGeoTrackingConfiguration()
        arView.session.run(config, options: [.resetTracking, .removeExistingAnchors])
    }
    
    
    func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
        // 主线程更新UI
        DispatchQueue.main.async {
            self.parent.viewModel.cameraTrackingStatus = camera.trackingState.description
        }
    }
    
    func session(_ session: ARSession, didChange geoTrackingStatus: ARGeoTrackingStatus) {
        // 主线程更新UI
        DispatchQueue.main.async {
            switch geoTrackingStatus.state {
                
            case .notAvailable:
                self.parent.viewModel.geoTrackingStatus = "Not Available"
            case .initializing:
                switch geoTrackingStatus.stateReason {
                    
                case .none:
                    self.parent.viewModel.geoTrackingStatus = "No issue reported"
                case .notAvailableAtLocation:
                    self.parent.viewModel.geoTrackingStatus = "Geotracking not available at this location"
                case .needLocationPermissions:
                    self.parent.viewModel.geoTrackingStatus = "Geotracking requires location permission"
                case .worldTrackingUnstable:
                    self.parent.viewModel.geoTrackingStatus = "World tracking is not stable"
                case .waitingForLocation:
                    self.parent.viewModel.geoTrackingStatus = "ARKit waiting for location"
                case .waitingForAvailabilityCheck:
                    self.parent.viewModel.geoTrackingStatus = "ARKit waiting for availablity check"
                case .geoDataNotLoaded:
                    self.parent.viewModel.geoTrackingStatus = "Geo data not loaded, please check network connection"
                case .devicePointedTooLow:
                    self.parent.viewModel.geoTrackingStatus = "Camera pointed too low"
                case .visualLocalizationFailed:
                    self.parent.viewModel.geoTrackingStatus = "Visual localization failed"
                @unknown default:
                    break
                }
            case .localizing:
                switch geoTrackingStatus.stateReason {
                    
                case .none:
                    self.parent.viewModel.geoTrackingStatus = "No issue reported"
                case .notAvailableAtLocation:
                    self.parent.viewModel.geoTrackingStatus = "Geotracking not available at this location"
                case .needLocationPermissions:
                    self.parent.viewModel.geoTrackingStatus = "Geotracking requires location permission"
                case .worldTrackingUnstable:
                    self.parent.viewModel.geoTrackingStatus = "World tracking is not stable"
                case .waitingForLocation:
                    self.parent.viewModel.geoTrackingStatus = "ARKit waiting for location"
                case .waitingForAvailabilityCheck:
                    self.parent.viewModel.geoTrackingStatus = "ARKit waiting for availablity check"
                case .geoDataNotLoaded:
                    self.parent.viewModel.geoTrackingStatus = "Geo data not loaded, please check network connection"
                case .devicePointedTooLow:
                    self.parent.viewModel.geoTrackingStatus = "Camera pointed too low"
                case .visualLocalizationFailed:
                    self.parent.viewModel.geoTrackingStatus = "Visual localization failed"
                @unknown default:
                    break
                }
            case .localized:
                // 补全状态文本
                self.parent.viewModel.geoTrackingStatus = "Localized"
                switch geoTrackingStatus.accuracy {
                    
                case .undetermined:
                    self.parent.viewModel.accuracy = "Undetermined"
                case .low:
                    self.parent.viewModel.accuracy = "Low"
                case .medium:
                    self.parent.viewModel.accuracy = "Medium"
                case .high:
                    self.parent.viewModel.accuracy = "High"
                @unknown default:
                    break
                }
            @unknown default:
                break
            }
        }
    }
    
}

其余扩展代码保持不变即可,修复后跟踪状态会实时在UI上刷新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 22:12:28