Updated 25 January 2020
In the Previous Blog, we have discussed how to draw the AR Map in the real-world & save this model for later use.
Previous Blogs Link: https://mobikul.com/ar-persistence-model-using-swift-4-2-part-1/
Now we have to load save the map on real-world for this we have to save the file by giving some unique name, in the previous blog we have taken the var mapID: String = “”
by using this we can differentiate AR Model.
For this Follow this Steps:
let mapID is “1” and you have to load this map on the real world.
1 2 3 4 5 6 |
class SuggestionARPathController: UIViewController { @IBOutlet weak var sceneView: ARSCNView! @IBOutlet var mapIndicator: UIButton! var mapID:String = "1" } |
1 2 3 4 5 6 7 8 9 |
func worldMapURL()->URL{ do { let id = mapID return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) .appendingPathComponent("worldMapURL"+id) } catch { fatalError("Error getting world map URL from document directory.") } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
func resetTrackingConfiguration(with worldMap: ARWorldMap? = nil) { let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = [.horizontal] let options: ARSession.RunOptions = [.resetTracking, .removeExistingAnchors] if let worldMap = worldMap { configuration.initialWorldMap = worldMap } sceneView.debugOptions = [.showFeaturePoints] sceneView.session.run(configuration, options: options) } |
1 2 3 4 5 |
func loadSaveMap(){ guard let worldMapData = retrieveWorldMapData(from: worldMapURL()), let worldMap = unarchive(worldMapData: worldMapData) else { return } resetTrackingConfiguration(with: worldMap) } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
extension SuggestionARPathController{ func displayProductDetailView()->SCNNode { let webViewPlane = SCNPlane(width: 0.15, height: 0.15) webViewPlane.cornerRadius = 0 let webViewNode = SCNNode(geometry: webViewPlane) // Set the web view as webViewPlane's primary texture webViewNode.geometry?.firstMaterial?.diffuse.contents = self.returnAnimationArrow() webViewNode.position.z -= 0.0 // position of images webViewNode.opacity = 0 webViewNode.eulerAngles.x = -.pi / 4 webViewNode.runAction(.sequence([ .wait(duration: 1.0), .fadeOpacity(to: 1.0, duration: 1.0), .moveBy(x: 0, y: 0, z: -0.00, duration: 1.0), .moveBy(x: 0, y: 0, z: -0.00, duration: 0.2) ]) ) return webViewNode } func returnAnimationArrow()->UIImageView{ let jeremyGif = UIImage.gifImageWithName("circle") let imageView = UIImageView(image: jeremyGif) imageView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) return imageView } } |
Note: Please add this extension in your Helper Swift file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
extension UIImage { public class func gifImageWithData(_ data: Data) -> UIImage? { guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { print("image doesn't exist") return nil } return UIImage.animatedImageWithSource(source) } public class func gifImageWithURL(_ gifUrl:String) -> UIImage? { guard let bundleURL:URL? = URL(string: gifUrl) else { print("image named \"\(gifUrl)\" doesn't exist") return nil } guard let imageData = try? Data(contentsOf: bundleURL!) else { print("image named \"\(gifUrl)\" into NSData") return nil } return gifImageWithData(imageData) } public class func gifImageWithName(_ name: String) -> UIImage? { guard let bundleURL = Bundle.main .url(forResource: name, withExtension: "gif") else { print("SwiftGif: This image named \"\(name)\" does not exist") return nil } guard let imageData = try? Data(contentsOf: bundleURL) else { print("SwiftGif: Cannot turn image named \"\(name)\" into NSData") return nil } return gifImageWithData(imageData) } class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double { var delay = 0.1 let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) let gifProperties: CFDictionary = unsafeBitCast( CFDictionaryGetValue(cfProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()), to: CFDictionary.self) var delayObject: AnyObject = unsafeBitCast( CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()), to: AnyObject.self) if delayObject.doubleValue == 0 { delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self) } delay = delayObject as! Double if delay < 0.1 { delay = 0.1 } return delay } class func gcdForPair(_ a: Int?, _ b: Int?) -> Int { var a = a var b = b if b == nil || a == nil { if b != nil { return b! } else if a != nil { return a! } else { return 0 } } if a < b { let c = a a = b b = c } var rest: Int while true { rest = a! % b! if rest == 0 { return b! } else { a = b b = rest } } } class func gcdForArray(_ array: Array<Int>) -> Int { if array.isEmpty { return 1 } var gcd = array[0] for val in array { gcd = UIImage.gcdForPair(val, gcd) } return gcd } class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? { let count = CGImageSourceGetCount(source) var images = [CGImage]() var delays = [Int]() for i in 0..<count { if let image = CGImageSourceCreateImageAtIndex(source, i, nil) { images.append(image) } let delaySeconds = UIImage.delayForImageAtIndex(Int(i), source: source) delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms } let duration: Int = { var sum = 0 for val: Int in delays { sum += val } return sum }() let gcd = gcdForArray(delays) var frames = [UIImage]() var frame: UIImage var frameCount: Int for i in 0..<count { frame = UIImage(cgImage: images[Int(i)]) frameCount = Int(delays[Int(i)] / gcd) for _ in 0..<frameCount { frames.append(frame) } } let animation = UIImage.animatedImage(with: frames, duration: Double(duration) / 1000.0) return animation } } |
1 2 3 4 5 6 7 8 9 10 |
override func viewDidLoad() { super.viewDidLoad() mapIndicator.backgroundColor = AppStaticColors.oneStar sceneView.delegate = self configureLighting() } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.loadSaveMap() } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
// /** Mobikul Single App @Category Webkul @author Webkul <[email protected]> FileName: SuggestionARPathController.swift Copyright (c) 2010-2018 Webkul Software Private Limited (https://webkul.com) @license https://store.webkul.com/license.html */ import UIKit import ARKit class SuggestionARPathController: UIViewController { @IBOutlet weak var sceneView: ARSCNView! @IBOutlet var mapIndicator: UIButton! var mapID:String = "1" @IBAction func dismissController(_ sender: UIButton) { self.dismiss(animated: true, completion: nil) } // func worldMapURL()->URL{ do { let id = mapID return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) .appendingPathComponent("worldMapURL"+id) } catch { fatalError("Error getting world map URL from document directory.") } } override func viewDidLoad() { super.viewDidLoad() mapIndicator.backgroundColor = AppStaticColors.oneStar sceneView.delegate = self configureLighting() } // func resetTrackingConfiguration(with worldMap: ARWorldMap? = nil) { let configuration = ARWorldTrackingConfiguration() configuration.planeDetection = [.horizontal] let options: ARSession.RunOptions = [.resetTracking, .removeExistingAnchors] if let worldMap = worldMap { configuration.initialWorldMap = worldMap mapIndicator.backgroundColor = AppStaticColors.fiveStar mapIndicator.setTitle(mapID, for: .normal) } else { mapIndicator.backgroundColor = AppStaticColors.oneStar } sceneView.debugOptions = [.showFeaturePoints] sceneView.session.run(configuration, options: options) } // func loadSaveMap(){ guard let worldMapData = retrieveWorldMapData(from: worldMapURL()), let worldMap = unarchive(worldMapData: worldMapData) else { return } resetTrackingConfiguration(with: worldMap) } func configureLighting() { sceneView.autoenablesDefaultLighting = true sceneView.automaticallyUpdatesLighting = true } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.loadSaveMap() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) sceneView.session.pause() } func archive(worldMap: ARWorldMap) throws { let data = try NSKeyedArchiver.archivedData(withRootObject: worldMap, requiringSecureCoding: true) try data.write(to: self.worldMapURL(), options: [.atomic]) } func retrieveWorldMapData(from url: URL) -> Data? { do { return try Data(contentsOf: self.worldMapURL()) } catch { return nil } } func unarchive(worldMapData data: Data) -> ARWorldMap? { guard let unarchievedObject = try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: data), let worldMap = unarchievedObject else { return nil } return worldMap } } extension SuggestionARPathController: ARSCNViewDelegate { func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) { guard !(anchor is ARPlaneAnchor) else { return } DispatchQueue.main.async { node.addChildNode(self.displayProductDetailView()) } } } extension SuggestionARPathController{ func displayProductDetailView()->SCNNode { let webViewPlane = SCNPlane(width: 0.15, height: 0.15) webViewPlane.cornerRadius = 0 let webViewNode = SCNNode(geometry: webViewPlane) // Set the web view as webViewPlane's primary texture webViewNode.geometry?.firstMaterial?.diffuse.contents = self.returnAnimationArrow() webViewNode.position.z -= 0.0 // position of images webViewNode.opacity = 0 webViewNode.eulerAngles.x = -.pi / 4 webViewNode.runAction(.sequence([ .wait(duration: 1.0), .fadeOpacity(to: 1.0, duration: 1.0), .moveBy(x: 0, y: 0, z: -0.00, duration: 1.0), .moveBy(x: 0, y: 0, z: -0.00, duration: 0.2) ]) ) return webViewNode } func returnAnimationArrow()->UIImageView{ let jeremyGif = UIImage.gifImageWithName("circle") let imageView = UIImageView(image: jeremyGif) imageView.frame = CGRect(x: 0, y: 0, width: 50, height: 50) return imageView } } |
Now your model will be load with corresponding Map Id.
If you have more details or questions, you can reply to the received confirmation email.
Back to Home
Be the first to comment.