Ant*_*wis 5 xcode ios swift arkit arworldmap
我找不到任何专门列出 ARKit 将其世界地图保存为的文件类型的文档。据我所知,它是一个.arexperience文件。本质上,我正在尝试修改文档浏览器,以便能够有选择地选择.arexperience要加载的文件。
我尝试启用支持的文档类型,特别是public.arexperience将 LSHandlerRank 作为类型字符串和值作为替代,以及将 CFBundleTypeRole 作为类型字符串和值作为查看器。
我还在info.plist Additional document type properties中将 Supports Document Browser 设置为YES。
9.7.18 编辑:我现在收到以下错误。对此有何想法?我已经清理了构建文件夹,关闭了 XCode,卸载并重新安装了项目,并重新启动了我的计算机,但没有任何变化。
9.13.18 编辑:此错误是由全新安装 XCode 10 Beta(包括 Swift 4.2)和您的示例项目引起的。知道发生了什么吗?研究表明 SIGABRT 错误是未使用的插座的结果,但使用让我查看连接的助理编辑器并没有显示明显的问题......
这似乎是一个有趣的问题,所以我想我应该尝试回答并提供一个完整的示例,可以为您和其他人未来的开发奠定基础。
这可能不是正确的方法,但希望它会有用。
话虽如此,我并不完全确定为什么要使用文档浏览器来允许用户选择ARWorldMaps。一种更简单的方法可能是将它们简单地存储在 中CoreData,并允许在UITableView例如中进行选择。或者将下面的逻辑合并到类似的内容中,例如当打开自定义文件时,将其保存到CoreData,并以这种方式显示所有收到的文件。
不管怎样,这里有一些东西可以让你开始更详细地探索这个主题。虽然,请注意,这并不是优化的方式,尽管它应该足以为您指明正确的方向^______^。
供你参考:
ARWorldMap 符合 NSSecureCoding 协议,因此您可以使用 NSKeyedArchiver 和 NSKeyedUnarchiver 类将世界地图与二进制数据表示形式相互转换。
由于我们想使用 aCustom UTI来保存ARWorldMap我们首先需要在我们的文件中进行设置,info.plist并将UTI类型设置为public.data.
在项目编辑器中看起来像这样:
有关执行此操作的更多信息,请参阅Ray Wenderlich提供的一个很好的教程提供的一个很好的教程。
完成此操作后,我们当然需要保存ARWorldMap并允许其导出。我创建了一个我们保存数据的方式,例如(我们的)typealias的键值String和值:DataARWorldMap
typealias BMWorlMapItem = [String: Data]
/// Saves An ARWorldMap To The Documents Directory And Allows It To Be Sent As A Custom FileType
@IBAction func saveWorldMap(){
//1. Attempt To Get The World Map From Our ARSession
augmentedRealitySession.getCurrentWorldMap { worldMap, error in
guard let mapToShare = worldMap else { print("Error: \(error!.localizedDescription)"); return }
//2. We Have A Valid ARWorldMap So Save It To The Documents Directory
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: mapToShare, requiringSecureCoding: true) else { fatalError("Can't Encode Map") }
do {
//a. Create An Identifier For Our Map
let mapIdentifier = "BlackMirrorzMap"
//b. Create An Object To Save The Name And WorldMap
var contentsToSave = BMWorlMapItem()
//c. Get The Documents Directory
let documentDirectory = try self.fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
//d. Create The File Name
let savedFileURL = documentDirectory.appendingPathComponent("/\(mapIdentifier).bmarwp")
//e. Set The Data & Save It To The Documents Directory
contentsToSave[mapIdentifier] = data
do{
let archive = try NSKeyedArchiver.archivedData(withRootObject: contentsToSave, requiringSecureCoding: true)
try archive.write(to: savedFileURL)
//f. Show An Alert Controller To Share The Item
let activityController = UIActivityViewController(activityItems: ["Check Out My Custom ARWorldMap", savedFileURL], applicationActivities: [])
self.present(activityController, animated: true)
print("Succesfully Saved Custom ARWorldMap")
}catch{
print("Error Generating WorldMap Object == \(error)")
}
} catch {
print("Error Saving Custom WorldMap Object == \(error)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
这还将数据保存到Documents Directory用户设备上,以便我们可以检查一切是否按预期运行,例如:
保存数据后,我们将向用户呈现一个,UIActivityAlertController以便用户可以将文件发送到他们的email等。
由于我们现在可以导出数据,因此当我们选择如何使用自定义处理程序打开数据时,我们需要处理如何接收数据:
我们的处理方式AppDelegate如下:
//---------------------------
//MARK: - Custom File Sharing
//---------------------------
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
//1. List Our Custom File Type Which Will Hold Our ARWorldMap
guard url.pathExtension == "bmarwp" else { return false }
//2. Post Our Data
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "MapReceived"), object: nil, userInfo: ["MapData" : url])
return true
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,当我们的自定义文件通过发送的AppDelegateaNotification被接收时,我们将在 ViewController 中注册,viewDidLoad例如:
NotificationCenter.default.addObserver(self, selector: #selector(importWorldMap(_:)), name: NSNotification.Name(rawValue: "MapReceived"), object: nil)
Run Code Online (Sandbox Code Playgroud)
现在我们已经完成了所有这些设置,我们当然需要提取数据以便可以使用它。这是这样实现的:
/// Imports A WorldMap From A Custom File Type
///
/// - Parameter notification: NSNotification)
@objc public func importWorldMap(_ notification: NSNotification){
//1. Remove All Our Content From The Hierachy
self.augmentedRealityView.scene.rootNode.enumerateChildNodes { (existingNode, _) in existingNode.removeFromParentNode() }
//2. Check That Our UserInfo Is A Valid URL
if let url = notification.userInfo?["MapData"] as? URL{
//3. Convert Our URL To Data
do{
let data = try Data(contentsOf: url)
//4. Unarchive Our Data Which Is Of Type [String: Data] A.K.A BMWorlMapItem
if let mapItem = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! BMWorlMapItem,
let archiveName = mapItem.keys.first,
let mapData = mapItem[archiveName] {
//5. Get The Map Data & Log The Anchors To See If It Includes Our BMAnchor Which We Saved Earlier
if let unarchivedMap = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [ARWorldMap.classForKeyedUnarchiver()], from: mapData),
let worldMap = unarchivedMap as? ARWorldMap {
print("Extracted BMWorldMap Item Named = \(archiveName)")
worldMap.anchors.forEach { (anchor) in if let name = anchor.name { print ("Anchor Name == \(name)") } }
//5. Restart Our Session
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .horizontal
configuration.initialWorldMap = worldMap
self.augmentedRealityView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
}
}catch{
print("Error Extracting Data == \(error)")
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们的数据已提取,我们只需要重新配置我们的Session并加载地图即可。
您会注意到,我正在记录AnchorNames, 作为检查过程是否成功的一种方法,因为我创建了一个自定义ARAnchor名称BMAnchor,我使用UITapGestureRecognizer如下方式创建:
//------------------------
//MARK: - User Interaction
//------------------------
/// Allows The User To Create An ARAnchor
///
/// - Parameter gesture: UITapGestureRecognizer
@objc func placeAnchor(_ gesture: UITapGestureRecognizer){
//1. Get The Current Touch Location
let currentTouchLocation = gesture.location(in: self.augmentedRealityView)
//2. Perform An ARSCNHiteTest For Any Feature Points
guard let hitTest = self.augmentedRealityView.hitTest(currentTouchLocation, types: .featurePoint).first else { return }
//3. Create Our Anchor & Add It To The Scene
let validAnchor = ARAnchor(name: "BMAnchor", transform: hitTest.worldTransform)
self.augmentedRealitySession.add(anchor: validAnchor)
}
Run Code Online (Sandbox Code Playgroud)
提取后,我会通过以下方式生成一个模型,这ARSCNViewDelegate对于检查我们的过程是否成功非常有用:
//-------------------------
//MARK: - ARSCNViewDelegate
//-------------------------
extension ViewController: ARSCNViewDelegate{
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
//1. Check We Have Our BMAnchor
if let name = anchor.name, name == "BMAnchor" {
//2. Create Our Model Node & Add It To The Hierachy
let modelNode = SCNNode()
guard let sceneURL = SCNScene(named: "art.scnassets/wavingState.dae") else { return nil }
for childNode in sceneURL.rootNode.childNodes { modelNode.addChildNode(childNode) }
return modelNode
}else{
return SCNNode()
}
}
}
Run Code Online (Sandbox Code Playgroud)
希望这能为您指明正确的方向......
这是一个完整的工作示例,供您和其他人尝试并适应您的需求:共享 ARWorldMaps
您需要做的就是等待会话开始运行,放置模型,然后按“保存”。当出现警报时,通过电子邮件将其发送给您自己,然后检查您的电子邮件,然后单击.bmarwp文件,该文件将在应用程序中自动加载 ^_________^,
在一个Document Based App,您可以非常轻松地使用自定义文件类型。
以下是您的 info.plist 的要求:
(a) 文件类型:
(b) 导出型尿路感染:
项目的信息部分如下所示:
因此,您最终会看到不同的屏幕,如下所示:
它既适用于我创建的基于基本文档的应用程序,也适用于Apple Files。
希望能帮助到你...