如何为SceneView中展示的.usdz文件截图?空白问题求助
解决SceneView展示USDZ模型时截图空白的问题
问题原因
- 当前代码通过重新创建
myView实例调用snapshot(),新实例的SceneView还未完成模型渲染,导致截图空白。 - SwiftUI的
drawHierarchy对Metal渲染的SceneView支持有限,无法正确捕获其渲染内容。
解决方案
方法一:利用SceneKit原生截图(推荐)
直接调用SCNView的原生snapshot()方法,这是最可靠的模型截图方式,步骤如下:
- 定义PreferenceKey传递底层SCNView实例
struct SCNViewPreferenceKey: PreferenceKey { static var defaultValue: SCNView? = nil static func reduce(value: inout SCNView?, nextValue: () -> SCNView?) { value = nextValue() ?? value } }
- 给View添加扩展,用于查找底层SCNView
extension View { var parentView: UIView? { guard let controller = UIApplication.shared.windows.first?.rootViewController else { return nil } return findParentView(in: controller.view) } private func findParentView(in view: UIView) -> UIView? { if let scnView = view as? SCNView { return scnView } for subview in view.subviews { if let found = findParentView(in: subview) { return found } } return nil } }
- 修改SceneView,传递底层视图引用
SceneView( scene: scene, options: [.allowsCameraControl, .autoenablesDefaultLighting] ) .background( GeometryReader { proxy in Color.clear.preference( key: SCNViewPreferenceKey.self, value: proxy.parentView as? SCNView ) } )
- 接收并保存SCNView实例,修改截图逻辑
@State private var scnView: SCNView? // 在视图中添加监听 .onPreferenceChange(SCNViewPreferenceKey.self) { newView in self.scnView = newView } // 按钮点击事件 Button("Take SS") { if let scnView = scnView { savedImage = scnView.snapshot() } }
方法二:修复SwiftUI快照逻辑
如果坚持使用SwiftUI快照方式,需确保捕获当前显示的视图而非重新创建的实例,可通过屏幕截图后裁剪实现:
extension View { func currentSnapshot() -> UIImage? { guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { return nil } let renderer = UIGraphicsImageRenderer(size: window.bounds.size) return renderer.image { context in window.drawHierarchy(in: window.bounds, afterScreenUpdates: true) } } }
调用示例:
Button("Take SS") { savedImage = currentSnapshot() // 需根据SceneView的位置和大小裁剪图片 }
完整修正代码
import SwiftUI import SceneKit struct SCNViewPreferenceKey: PreferenceKey { static var defaultValue: SCNView? = nil static func reduce(value: inout SCNView?, nextValue: () -> SCNView?) { value = nextValue() ?? value } } extension View { var parentView: UIView? { guard let controller = UIApplication.shared.windows.first?.rootViewController else { return nil } return findParentView(in: controller.view) } private func findParentView(in view: UIView) -> UIView? { if let scnView = view as? SCNView { return scnView } for subview in view.subviews { if let found = findParentView(in: subview) { return found } } return nil } } struct USDZModelView: View { @State private var scene: SCNScene? @State private var scnView: SCNView? @State var savedImage: UIImage? init(url: URL) { do { self._scene = State(initialValue: try SCNScene(url: url, options: nil)) self.scene?.background.contents = UIColor.gray } catch { print("模型加载失败:\(error)") } } var body: some View { NavigationStack { VStack { if let scene = scene { SceneView( scene: scene, options: [.allowsCameraControl, .autoenablesDefaultLighting] ) .background( GeometryReader { proxy in Color.clear.preference( key: SCNViewPreferenceKey.self, value: proxy.parentView as? SCNView ) } ) } else { Text("模型加载失败") } Button("Take SS") { if let scnView = scnView { savedImage = scnView.snapshot() } } } .onPreferenceChange(SCNViewPreferenceKey.self) { newView in self.scnView = newView } if let img = savedImage { Image(uiImage: img) .border(Color.red) .background(.purple) .padding() } } .navigationBarBackButtonHidden(true) } }
内容的提问来源于stack exchange,提问作者Abdul91
相关产品推荐
相关产品推荐

