You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为SceneView中展示的.usdz文件截图?空白问题求助

解决SceneView展示USDZ模型时截图空白的问题

问题原因

  • 当前代码通过重新创建myView实例调用snapshot(),新实例的SceneView还未完成模型渲染,导致截图空白。
  • SwiftUI的drawHierarchy对Metal渲染的SceneView支持有限,无法正确捕获其渲染内容。

解决方案

方法一:利用SceneKit原生截图(推荐)

直接调用SCNView的原生snapshot()方法,这是最可靠的模型截图方式,步骤如下:

  1. 定义PreferenceKey传递底层SCNView实例
struct SCNViewPreferenceKey: PreferenceKey {
    static var defaultValue: SCNView? = nil
    static func reduce(value: inout SCNView?, nextValue: () -> SCNView?) {
        value = nextValue() ?? value
    }
}
  1. 给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
    }
}
  1. 修改SceneView,传递底层视图引用
SceneView(
    scene: scene,
    options: [.allowsCameraControl, .autoenablesDefaultLighting]
)
.background(
    GeometryReader { proxy in
        Color.clear.preference(
            key: SCNViewPreferenceKey.self,
            value: proxy.parentView as? SCNView
        )
    }
)
  1. 接收并保存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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:23:18