ARKit camera tracking state can’t show on SwiftUI

I’m trying to show the camera tracking state info on top of device screen, but still get errors for all my efforts. I’m very confused with how ARView works with SwiftUI, for showing AR experience and adding button icons on top of it is simple, but to getting data from session just looks more work need to be done, could someone help me this? Thanks! Error in ViewModel: ‘description is a get-only property’

ContentView

import SwiftUI
import ARKit
import RealityKit

struct ContentView: View {
    @StateObject var vm = ViewModel()
    
    var body: some View {
        ZStack{
            ARViewContainer()
                .edgesIgnoringSafeArea(.all)
                .environmentObject(vm)
            VStack{
                Text(vm.sessionInfoLabel)
                    .font(.title3)
                    .foregroundColor(.red)
                
                Spacer()
                HStack{
                    Button{
                        
                    } label: {
                        Image(systemName: "person.2")
                            .padding()
                            .font(.title)
                    }
                    Spacer()
                    Button{
                        
                    } label: {
                        Image(systemName: "target")
                            .padding()
                            .font(.title)
                    }
                }
                .padding()
                
            }
        }
    }
}

struct ARViewContainer: UIViewRepresentable {
    @EnvironmentObject var vm: ViewModel
    func makeUIView(context: Context) -> ARView {
        return vm.arView
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
    }
}

ViewModel

import SwiftUI
import ARKit
import RealityKit

class ViewModel: ObservableObject {
    @Published var arView: ARView
    var sessionInfoLabel = ""
    init() {
        arView = ARView.init(frame: .zero)
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = .horizontal
        arView.session.delegate = arView
        arView.session.run(config)
    }
}


extension ARView: ARSessionDelegate {
    public func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
        camera.trackingState.description = ViewModel().sessionInfoLabel
    }
}

extension ARCamera.TrackingState: CustomStringConvertible {
    public var description: String {
        switch self {
        case .normal:
            return "Normal"
        case .notAvailable:
            return "Not Available"
        case .limited(.initializing):
            return "Initializing"
        case .limited(.excessiveMotion):
            return "Excessive Motion"
        case .limited(.insufficientFeatures):
            return "Insufficient Features"
        case .limited(.relocalizing):
            return "Relocalizing"
        case .limited:
            return "Unspecified Reason"
        }
    }
}

Accepted Reply

Hello Franklan010,

There are a few issues in the code you posted. For one, the compiler error you are receiving is because you are trying to set camera.trackingState.description, but you have defined description as a get-only property.

In any case, I have put together a short snippet that more or less demonstrates the basics of how you can integrate an ARView with SwiftUI, and utilize ARSession state to update your views (in this case, a Text view):

struct ContentView: View {
    @State var viewModel = ARViewContainer.ViewModel()
    
    var body: some View {
        ZStack {
            ARViewContainer(viewModel: $viewModel).ignoresSafeArea()
            Text(viewModel.trackingState?.description ?? "")
                .font(.headline)
                .foregroundColor(.green)
        }
    }
}

struct ARViewContainer: UIViewRepresentable {
    
    struct ViewModel {
        var trackingState: ARCamera.TrackingState? = nil
    }
    
    @Binding var viewModel: ViewModel
    
    func makeUIView(context: Context) -> ARView {
        
        // Create the view.
        let view = ARView(frame: .zero)
        
        // Set the coordinator as the session delegate.
        view.session.delegate = context.coordinator
        
        // Return the view.
        return view
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    final class Coordinator: NSObject, ARSessionDelegate {

        var parent: ARViewContainer

        init(_ parent: ARViewContainer) {
            self.parent = parent
        }

        func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
            parent.viewModel.trackingState = camera.trackingState
        }
    }
}
  • Thank you so much @gchiste, I copied you code snippet to a new file and there was an error says “trackingState doesn’t have description”. However, it has debugDescription., and the text view showing a little too much info. Anyway, thanks for helping me out.

  • Yes, I did not include the extension that you had in your code snippet, which defined a "description" property on ARCamera.TrackingState, you can use that extension if you'd like.

  • Thanks @gchiste, it works as I expected after adding extension. Looks so good, thanks again.

Add a Comment

Replies

Hello Franklan010,

There are a few issues in the code you posted. For one, the compiler error you are receiving is because you are trying to set camera.trackingState.description, but you have defined description as a get-only property.

In any case, I have put together a short snippet that more or less demonstrates the basics of how you can integrate an ARView with SwiftUI, and utilize ARSession state to update your views (in this case, a Text view):

struct ContentView: View {
    @State var viewModel = ARViewContainer.ViewModel()
    
    var body: some View {
        ZStack {
            ARViewContainer(viewModel: $viewModel).ignoresSafeArea()
            Text(viewModel.trackingState?.description ?? "")
                .font(.headline)
                .foregroundColor(.green)
        }
    }
}

struct ARViewContainer: UIViewRepresentable {
    
    struct ViewModel {
        var trackingState: ARCamera.TrackingState? = nil
    }
    
    @Binding var viewModel: ViewModel
    
    func makeUIView(context: Context) -> ARView {
        
        // Create the view.
        let view = ARView(frame: .zero)
        
        // Set the coordinator as the session delegate.
        view.session.delegate = context.coordinator
        
        // Return the view.
        return view
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    final class Coordinator: NSObject, ARSessionDelegate {

        var parent: ARViewContainer

        init(_ parent: ARViewContainer) {
            self.parent = parent
        }

        func session(_ session: ARSession, cameraDidChangeTrackingState camera: ARCamera) {
            parent.viewModel.trackingState = camera.trackingState
        }
    }
}
  • Thank you so much @gchiste, I copied you code snippet to a new file and there was an error says “trackingState doesn’t have description”. However, it has debugDescription., and the text view showing a little too much info. Anyway, thanks for helping me out.

  • Yes, I did not include the extension that you had in your code snippet, which defined a "description" property on ARCamera.TrackingState, you can use that extension if you'd like.

  • Thanks @gchiste, it works as I expected after adding extension. Looks so good, thanks again.

Add a Comment