How do I save and load ARWorldMap in SwiftUI app?

岁酱吖の 提交于 2021-02-19 05:38:26

问题


I followed a YouTube tutorial on how to place virtual Models in SwiftUI.

Now that I can place my Models, I would like to save and load the models position.

I have already made 2 Buttons for save and load, but I don't know the correct code to save and load the Entities and Anchors.

The following code is inside my updateUIView function:

func updateUIView(_ uiView: ARView, context: Context) {

    if let name = self.modelName {
              
        print("Modell mit dem Namen \(name) wurde zum plaziert")
                  
         let filename = name + ".usdz"
         let modelEntity = try! ModelEntity.loadModel(named: filename)
              
         modelEntity.generateCollisionShapes(recursive: true)
         uiView.installGestures(.all, for: modelEntity)                  
              
         let anchorEntity = AnchorEntity(plane: .any)
         anchorEntity.addChild(modelEntity)
              
         uiView.scene.addAnchor(anchorEntity)            
    }
}

I tried it with an "else"-statement, since I had the "if" in the beginning. But this did not work out. I made a state variable (type Boolean), which triggers when I press the save or load button. but I can't get the connection to the updateUIView-function right.

Update 1:

I implemented the save and load functions inside my "updateUIView" function like this:

struct ARViewContainer: UIViewRepresentable {
    
    @Binding var reset: Bool
    @Binding var modelSelector: String
    @Binding var save: Bool
    @Binding var load: Bool

    let config = ARWorldTrackingConfiguration()


    
    
    
    
    func makeUIView(context: Context) -> ARView {
        
        
        
        // create config for all entities
        
        
         let arView = ARView(frame: .zero)


            config.planeDetection = [.horizontal, .vertical]
            config.environmentTexturing = .automatic
            config.sceneReconstruction = .mesh
        
        
        
        arView.session.run(config)
        
        
        
        return arView
        
    }
    

    
    
    // App kontinuierlich beobachten
    func updateUIView(_ uiView: ARView, context: Context) {
        
        
        // create anchor and model-Entity
        if modelSelector != "default" {
            
            let modelEntity = try! ModelEntity.loadModel(named: "\(modelSelector)")
            modelEntity.name = ("\(modelSelector)")
            modelEntity.generateCollisionShapes(recursive: true)
            uiView.installGestures(.all, for: modelEntity)
            let anchor  = AnchorEntity(plane: .any)
            anchor.addChild(modelEntity)
                
            uiView.scene.addAnchor(anchor)
            
        }
    // reset anchor
    if reset == true {
        
        uiView.scene.anchors.removeAll()
        
        }
      
        //MARK: saveload
            if save == true {
            uiView.session.getCurrentWorldMap { (worldMap, _) in
                
                if let map: ARWorldMap = worldMap {
                    
                    let data = try! NSKeyedArchiver.archivedData(withRootObject: map,
                                                          requiringSecureCoding: true)
                    
                    let savedMap = UserDefaults.standard
                    savedMap.set(data, forKey: "WorldMap")
                    savedMap.synchronize()
                }
            }
        }
        
        if load == true {
            let storedData = UserDefaults.standard

                if let data = storedData.data(forKey: "WorldMap") {

                    if let unarchiver = try? NSKeyedUnarchiver.unarchivedObject(
                                           ofClasses: [ARWorldMap.classForKeyedUnarchiver()],
                                                from: data),
                       let worldMap = unarchiver as? ARWorldMap {

                            config.initialWorldMap = worldMap
                            uiView.session.run(config)
                    }
                }
            }
        
            //MARK:
        }

    
}

By writing the code directly into the updateUIView function I could use the uiView instead of the arView. Only the config constant had to be outside of the makrUIView function. The Boolean for load and save are set to false but will be true, when I press the corresponding buttons:

struct SaveLoadReset: View {

    @Binding var reset: Bool
    @Binding var save: Bool
    @Binding var load: Bool
    @Binding var modelSelector: String
    
    var body: some View {
        
            
            
    //HStack für den Laden und Speichern Knopf
                    HStack(spacing: 10){
        
    //save-Button:
                        Button(action: {
                            print("DEBUG: saveButton")
                            self.modelSelector = "default"
                            self.save = true
                            print("DEBUG: save = \(self.save)")
                        })
                        {
                            Image(systemName: "square.and.arrow.up")
                                
                            .padding(20)
                            .background(Color.white)
                            .opacity(0.3)
                            .cornerRadius(20)
                            .padding(10)
                            .font(.title)
                        }
                            
                        
                        
    //load-Button:
                        Button(action: {
                            print("DEBUG: loadButton")
                            self.load = true
                            print("DEBUG: load = \(self.load)")
                        })
                        {
                            Image(systemName: "square.and.arrow.down")
                                
                            .padding(20)
                            .background(Color.white)
                            .opacity(0.3)
                            .cornerRadius(20)
                            .font(.title)
                        }
                        
                        

                        Spacer()
                        
    //reset-Button:
                        Button(action: {
                            
                            print("DEBUG: removeButton")
                            self.reset = true
                            
                            print("DEBUG: reset = \(self.reset)" )
                            
                            
                        })
                        {
                            Image(systemName: "arrow.clockwise.circle")
                                
                            .padding(20)
                            .background(Color.white)
                            .opacity(0.3)
                            .cornerRadius(20)
                            .font(.title)
                            .padding(10)
                        }
                        
                    }
    }
}

I don't get any error messages so far, but my code does not seem to work. I can press the buttons, but the models and anchors do not get reloaded after restarting the app.


回答1:


Your code might look like this:

import RealityKit
import SwiftUI
import ARKit

struct ARViewContainer: UIViewRepresentable {

    @Binding var saved: Bool
    @Binding var loaded: Bool
    
    let anchor = AnchorEntity(world: [0, 0,-2])
    let arView = ARView(frame: .zero)
    let config = ARWorldTrackingConfiguration()
    
    func makeUIView(context: Context) -> ARView {
        
        let sphere = MeshResource.generateSphere(radius: 1)
        let sphereEntity = ModelEntity(mesh: sphere)
        sphereEntity.name = "SPHERE"
        sphereEntity.setParent(anchor)
        arView.scene.anchors.append(anchor)
        return arView
    }        
    fileprivate func saveWorldMap() {
        print("SAVED")
        DispatchQueue.main.async {
            saved = false
        }
    }
    fileprivate func loadWorldMap() {
        print("LOADED")
        DispatchQueue.main.async {
            loaded = false
        }
    } 
   
    func updateUIView(_ uiView: ARView, context: Context) {
        
        // Retrieve the entity from your scene
        guard let entity = uiView.scene.findEntity(named: "SPHERE")
        else { return }
                        
        if saved {
            self.saveWorldMap()
        }
        if loaded {
            self.loadWorldMap()
        }
    }
}

struct ContentView : View {
    
    @State private var saver = false
    @State private var loader = false
    
    var body: some View {
        VStack {
            ARViewContainer(saved: $saver, loaded: $loader)
            HStack {
                Spacer()
                Button(action: { self.saver.toggle() }) {
                    Text("Save")
                }
                Spacer()
                Button(action: { self.loader.toggle() }) {
                    Text("Load")
                }
                Spacer()
            }
        }
    }
}

The content of saveWorldMap() and loadWorldMap() methods might be like that:

fileprivate func saveWorldMap() {

    arView.session.getCurrentWorldMap { (worldMap, _) in
        
        if let map: ARWorldMap = worldMap {
            
            let data = try! NSKeyedArchiver.archivedData(withRootObject: map, 
                                                  requiringSecureCoding: true)
            
            let savedMap = UserDefaults.standard
            savedMap.set(data, forKey: "WorldMap")
            savedMap.synchronize()
        }
    }
}

fileprivate func loadWorldMap() {

    let storedData = UserDefaults.standard

    if let data = storedData.data(forKey: "WorldMap") {

        if let unarchiver = try? NSKeyedUnarchiver.unarchivedObject(
                               ofClasses: [ARWorldMap.classForKeyedUnarchiver()],
                                    from: data),
           let worldMap = unarchiver as? ARWorldMap {

                config.initialWorldMap = worldMap
                arView.session.run(config)
        }
    }
}


来源:https://stackoverflow.com/questions/63730554/how-do-i-save-and-load-arworldmap-in-swiftui-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!