如何在QLPreviewController中动态修改RealityKit实体文本并展示
能否在QLPreviewController中动态修改Reality Composer场景的实体文本?
之前在Stack Overflow上看到过关于在Reality Composer场景中动态修改实体文本的优质问答,现在想了解是否可以在QLPreviewController而非ARView中实现相同功能?
我当前的代码是从URL加载预览项并载入QLPreviewController,希望在返回预览项进行展示前,动态修改.rcproject文件中包含的气泡文本,代码如下:
class ARQLViewController: UIViewController, QLPreviewControllerDataSource, QLPreviewControllerDelegate { var allowsContentScaling = true var webViewModel: WebViewModel init(webViewModelReceived: WebViewModel) { webViewModel = webViewModelReceived super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidAppear(_ animated: Bool) { setupOnAppear() } func setupOnAppear() { let previewController = QLPreviewController() previewController.dataSource = self previewController.delegate = self present(previewController, animated: true, completion: nil) } func numberOfPreviewItems(in controller: QLPreviewController) -> Int { return 1 } func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { let url = webViewModel.afUrlForAR let previewItem = ARQuickLookPreviewItem(fileAt: url!) previewItem.allowsContentScaling = allowsContentScaling return previewItem } }
解决方案
QLPreviewController本身无法直接修改内存中的Reality Composer场景内容,它是基于文件路径直接加载展示的。要实现动态修改文本的需求,必须先修改本地文件的内容,再将修改后的文件传给预览控制器,具体步骤如下:
核心思路
- 复制原.rcproject文件到临时目录:原文件可能处于只读状态(比如来自App Bundle或网络下载),需要复制到App沙盒的可写临时目录。
- 修改临时文件中的文本内容:.rcproject本质是一个Bundle包,内部包含存储场景数据的plist文件,找到对应文本节点修改值。
- 用修改后的临时文件创建预览项:将临时文件路径传入ARQuickLookPreviewItem,再返回给QLPreviewController。
修改后的代码示例
class ARQLViewController: UIViewController, QLPreviewControllerDataSource, QLPreviewControllerDelegate { var allowsContentScaling = true var webViewModel: WebViewModel init(webViewModelReceived: WebViewModel) { webViewModel = webViewModelReceived super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidAppear(_ animated: Bool) { setupOnAppear() } func setupOnAppear() { let previewController = QLPreviewController() previewController.dataSource = self previewController.delegate = self present(previewController, animated: true, completion: nil) } func numberOfPreviewItems(in controller: QLPreviewController) -> Int { return 1 } func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem { guard let originalUrl = webViewModel.afUrlForAR else { fatalError("AR file URL is nil") } // 1. 复制原文件到临时目录 let tempDir = FileManager.default.temporaryDirectory let tempFileName = originalUrl.lastPathComponent let tempUrl = tempDir.appendingPathComponent(tempFileName) do { // 清理已存在的临时文件 if FileManager.default.fileExists(atPath: tempUrl.path) { try FileManager.default.removeItem(at: tempUrl) } // 复制原文件到临时目录 try FileManager.default.copyItem(at: originalUrl, to: tempUrl) // 2. 修改临时文件中的文本内容 modifyRCProjectText(at: tempUrl, newText: "你的动态文本内容") } catch { print("文件操作失败: \(error.localizedDescription)") } // 3. 使用修改后的文件创建预览项 let previewItem = ARQuickLookPreviewItem(fileAt: tempUrl) previewItem.allowsContentScaling = allowsContentScaling return previewItem } /// 修改.rcproject包内的文本内容 private func modifyRCProjectText(at projectUrl: URL, newText: String) { // 获取rcproject包内的场景plist文件 let projectBundle = Bundle(url: projectUrl) guard let scenePlistPath = projectBundle?.path(forResource: "Scene", ofType: "plist") else { print("找不到Scene.plist文件") return } // 读取并解析plist数据 guard var sceneData = NSMutableDictionary(contentsOfFile: scenePlistPath), var entities = sceneData["entities"] as? [[String: Any]] else { print("无法读取场景数据") return } // 遍历实体,找到文本组件并修改内容 for (index, var entity) in entities.enumerated() { if var components = entity["components"] as? [[String: Any]] { for (compIndex, var component) in components.enumerated() { if component["type"] as? String == "text", var properties = component["properties"] as? [String: Any] { // 更新文本值 properties["text"] = newText component["properties"] = properties components[compIndex] = component entity["components"] = components entities[index] = entity } } } } // 将修改后的数据写回plist sceneData["entities"] = entities sceneData.write(toFile: scenePlistPath, atomically: true) } }
注意事项
- .rcproject的内部结构可能随Reality Composer版本变化,需要根据你自己的场景实际结构调整
modifyRCProjectText方法中的遍历逻辑。 - 确保文件操作处理好异常情况,避免因权限问题或文件损坏导致预览失败。
- 如果原文件来自网络,要确保文件完全下载完成后再执行复制和修改操作。
内容的提问来源于stack exchange,提问作者D. Rothschild
相关产品推荐
相关产品推荐

