如何在SwiftUI应用中保存和加载ARWorldMap? [英] How do I save and load ARWorldMap in SwiftUI app?
问题描述
我遵循了YouTube教程,内容涉及如何在SwiftUI中放置虚拟模型.
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.
我已经制作了2个用于保存和加载的按钮,但是我不知道正确的代码来保存和加载实体和锚点.
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.
以下代码位于我的updateUIView函数内部:
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)
}
}
我尝试了"else"语句,因为我有"if"语句.在一开始的时候.但这并没有解决.我创建了一个状态变量(布尔型),当我按下保存"或加载"按钮时会触发该状态变量.但是我无法正确连接到updateUIView功能.
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.
我在"updateUIView"内部实现了保存和加载功能.这样的功能:
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:
}
}
通过直接将代码编写到updateUIView函数中,我可以使用uiView代替arView.只有config常量必须在makrUIView函数之外.当我按下相应的按钮时,用于加载和保存的布尔值设置为false,但将为true:
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.
推荐答案
您的代码可能如下所示:
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()
}
}
}
}
saveWorldMap()和 loadWorldMap()
方法的内容可能是这样的:
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)
}
}
}
这篇关于如何在SwiftUI应用中保存和加载ARWorldMap?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!