AR Kit 2.0跟踪图片批量下载转换优化方案咨询
我近期开发了一款支持自定义图片与文字创建AR对象的AR应用,目前应用会生成QR码并将其图片存储至服务器。我希望实现将这些图片取回并存储到AR Resource Group文件夹的功能。
当前方案是下载全部图片,转换为ARReferenceImage类型后,基于该图片列表运行AR Session。但随着用户与AR创作内容增多,下载耗时大幅增加,用户体验越来越差。
以下是我用于取回并转换所有图片类型的代码:
func getAllQRCodes(completion: @escaping CompletionHandler) { var downloaded = 0 let reference = Database.database().reference().child("QR") reference.observe(DataEventType.value, with: { (snapshot) in let postDict = snapshot.value as? [String : String] ?? [:] DispatchQueue.global(qos: .background).async { for (key, value) in postDict { print("downloaded: \(downloaded) / total: \(postDict.count)") let percentage = (Double(downloaded)/Double(postDict.count)) * 100 DispatchQueue.main.async { self.qrProgressCircle.startProgress(to: CGFloat(percentage), duration: 1) } if self.downloadURLs.contains(value) == false { self.downloadURLs.insert(value) let qrImage = UIImage(url: URL(string: value)) let qrCiImage = CIImage(image: qrImage!) let qrCGImage = self.convertCIImageToCGImage(inputImage: qrCiImage!) let qrARImage = ARReferenceImage(qrCGImage!, orientation: CGImagePropertyOrientation.up, physicalWidth: 0.2) qrARImage.name = key self.ARTrackingImages.insert(qrARImage) downloaded += 1 } } completion(true) } }) }
我的问题是:是否存在更优实现方案?比如在后台线程执行操作并持续重启sceneView session?这是否会导致糟糕的用户体验?
更优实现方案推荐
针对你当前的问题,我有几个具体的优化方向,既能减少下载耗时,又能提升用户体验:
1. 改用增量下载,避免全量同步
当前全量下载所有QR图片的方式,随着内容增多必然越来越慢。你可以:
- 在服务器端给每个QR记录添加最后更新时间戳或唯一标识(比如版本号)
- 本地保存上次同步的时间戳/版本号,每次只从服务器拉取新增或更新的QR图片
- 用数据库的
queryOrdered(byChild:)和queryStarting(atValue:)方法过滤出需要下载的内容,而不是获取整个QR节点的全部数据
示例思路代码:
func getNewQRCodes(lastSyncTimestamp: TimeInterval, completion: @escaping CompletionHandler) { let reference = Database.database().reference().child("QR") // 假设服务器每个QR节点有"updatedAt"字段记录更新时间 reference.queryOrdered(byChild: "updatedAt").queryStarting(atValue: lastSyncTimestamp) .observeSingleEvent(of: .value) { snapshot in // 只处理新增/更新的内容 let newPostDict = snapshot.value as? [String: [String: Any]] ?? [:] // 后续下载转换逻辑... // 更新本地lastSyncTimestamp为当前时间 } }
2. 并行异步下载,提升下载效率
你当前的代码是串行循环下载图片,效率极低。可以用OperationQueue或URLSession的并行任务来同时下载多个图片:
- 创建一个后台
OperationQueue,设置合理的最大并发数(比如5-10,避免服务器压力过大) - 每个图片下载作为一个独立的
Operation,完成后转换为ARReferenceImage - 注意对
downloadURLs、ARTrackingImages这些共享集合的线程安全访问(比如用串行队列同步读写)
示例代码片段:
let downloadQueue = OperationQueue() downloadQueue.maxConcurrentOperationCount = 5 downloadQueue.qualityOfService = .background for (key, value) in newPostDict { guard !self.downloadURLs.contains(value) else { continue } let operation = BlockOperation { [weak self] in guard let self = self, let url = URL(string: value) else { return } do { let data = try Data(contentsOf: url) guard let qrImage = UIImage(data: data), let ciImage = CIImage(image: qrImage) else { return } let cgImage = self.convertCIImageToCGImage(inputImage: ciImage) guard let arImage = ARReferenceImage(cgImage!, orientation: .up, physicalWidth: 0.2) else { return } arImage.name = key // 线程安全更新共享集合 DispatchQueue.main.sync { self.downloadURLs.insert(value) self.ARTrackingImages.insert(arImage) // 更新进度 let downloadedCount = self.ARTrackingImages.count let percentage = Double(downloadedCount)/Double(newPostDict.count) * 100 self.qrProgressCircle.startProgress(to: CGFloat(percentage), duration: 0.5) } } catch { print("下载失败: \(error)") } } downloadQueue.addOperation(operation) }
3. 动态更新AR Session,无需重启
完全不需要重启SceneView的Session!ARKit支持在Session运行时动态添加参考图片,用户可以在下载过程中就开始使用已下载好的QR码进行AR追踪,不用等全部下载完成。
使用session.add(_:)方法添加单个或多个ARReferenceImage:
// 当一个ARReferenceImage转换完成后,直接添加到当前Session func addARImageToSession(_ arImage: ARReferenceImage) { guard let configuration = self.sceneView.session.configuration as? ARImageTrackingConfiguration else { return } // 如果是首次添加,初始化trackingImages数组 if configuration.trackingImages == nil { configuration.trackingImages = Set<ARReferenceImage>() } configuration.trackingImages?.insert(arImage) // 重新运行配置(不会中断当前追踪,只是更新可识别的图片) self.sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors]) }
注意:这里的
run方法使用.resetTracking选项只会重置图片追踪状态,不会重启整个相机会话,用户几乎感知不到中断。
4. 本地缓存已转换的ARReferenceImage
把已经转换好的ARReferenceImage序列化存储到本地(比如Documents目录),下次启动App时直接加载缓存,不用重新下载和转换:
- 利用
ARReferenceImage的rawData属性获取序列化数据 - 将数据保存到本地文件,文件名用QR的key作为标识
- 启动App时先加载所有本地缓存的
ARReferenceImage,再进行增量同步
关于“后台线程操作+持续重启Session”的问题
频繁重启SceneView的Session会导致非常糟糕的用户体验:
- 每次重启都会中断相机预览,出现短暂的黑屏或卡顿
- AR追踪会被重置,之前识别的AR对象会消失,需要用户重新扫描
- 频繁的状态切换会让用户感到困惑,降低App的流畅感
而上面提到的动态添加参考图片的方式,完全避免了重启Session的必要,是更优的选择。
内容的提问来源于stack exchange,提问作者Antonio Chan

