Это казалось интересным вопросом, поэтому я подумал, что мне стоит ответить и иметь полностью рабочий пример, который может послужить основой для будущего развития вами и другими.
Возможно, это неправильный подход, но, надеюсь, он будет полезен.
Сказав это, я не совсем уверен, почему вы хотите использовать Браузер документов, чтобы позволить пользователю выбрать ARWorldMaps
. Более простой подход может состоять в том, чтобы просто сохранить их в CoreData
и разрешить выбор в UITableView
, например. Или включите приведенную ниже логику в нечто подобное, например, когда пользовательский файл открыт, сохраните его в CoreData
и представьте все полученные файлы таким образом.
В любом случае, здесь есть кое-что, что может начать ваше изучение этой темы более подробно. Хотя, пожалуйста, обратите внимание, что это никак не оптимизировано, хотя этого должно быть более чем достаточно, чтобы указать вам правильное направление ^ ______ ^.
Для вашей информации:
ARWorldMap соответствует протоколу NSSecureCoding, поэтому вы можете конвертировать
карта мира в или из двоичного представления данных с использованием
Классы NSKeyedArchiver и NSKeyedUnarchiver.
Поскольку мы хотим использовать Custom UTI
для сохранения нашего ARWorldMap
, нам сначала нужно установить его в нашем файле info.plist
, где мы устанавливаем тип UTI
на public.data
.
В редакторе проекта это выглядит так:
Дополнительную информацию о том, как это сделать, можно найти здесь: Ray Wenderlich .
Сделав это, мы, конечно, должны сохранить наш ARWorldMap
и разрешить его экспорт. Я создал typealias
, как мы будем сохранять наши данные, например. ключевое значение String
и значение Data
(наше ARWorldMap
):
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)")
}
}
}
Это также сохраняет данные в 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
}
Как вы можете видеть, когда наш пользовательский файл получен через AppDelegate
, отправляется Notification
, который мы зарегистрируем в нашем ViewController viewDidLoad
, например:
NotificationCenter.default.addObserver(self, selector: #selector(importWorldMap(_:)), name: NSNotification.Name(rawValue: "MapReceived"), object: nil)
Теперь у нас есть все эти настройки, которые нам, конечно, нужны для извлечения данных, чтобы их можно было использовать. Что достигается так:
/// 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)")
}
}
}
Теперь наши данные извлечены, нам просто нужно перенастроить нашу 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)
}
Когда это извлекается, я затем генерирую модель с помощью 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()
}
}
}
Надеюсь, это укажет вам правильное направление ...
А вот полный рабочий пример для вас и всех остальных, с которыми можно экспериментировать и адаптироваться к вашим потребностям: Совместное использование ARWorldMaps
Все, что вам нужно сделать, это дождаться начала сеанса, поместить вашу модель и нажать Сохранить. Получив оповещение, отправьте его по электронной почте себе, а затем проверьте свою электронную почту и щелкните файл .bmarwp
, который автоматически загрузится в приложении ^ _________ ^,
В Document Based App
вы можете легко использовать пользовательские типы файлов.
Ниже приведены требования для вашего info.plist:
(a) Типы документов:
(b) Экспортируемые ИМП типа:
Раздел информации о вашем проекте выглядит так:
Таким образом, вы в конечном итоге получите разные экраны, например:
Работает как в основном приложении на основе документов, которое я создал, так и в приложении Apple Files
.
Надеюсь, это поможет ...