Goal: Taking screenshot of a .usdz file displayed in SceneView
Issue: Empty/Blank screenshot image
Full Context:
I have been stuck at this problem for a couple of days now, have tried every solution I can find out there, tried to devise my own as well but nothing has worked so far. My goal is a simple one: Taking screenshot of a .usdz file being displayed in a SwiftUI project. I am loading up the .usdz file in SCNScene
and displaying that scene in a SceneView
, when I try to take screenshot of that SCNScene
directly or via any of its hierarchal containers, screenshot ends up empty/blank. Whereas same code for taking screenshot works on any other view in the app.
Here is my the code of my file that I cleaned up to make it relevant to the problem:
import SwiftUI
import SceneKit
struct USDZModelView: View {
@State private var scene: SCNScene?
@State var takeSnapshot: Bool = false
@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 {
}
}
var body: some View {
NavigationStack {
let myView = VStack {
if let scene = scene {
SceneView(
scene: scene,
options: [.allowsCameraControl, .autoenablesDefaultLighting]
)
} else {
}
Button("Take SS") {
takeSnapshot = true
}
}
myView
.onChange(of: takeSnapshot) {
let img = myView.snapshot()
savedImage = img
// View the image however, upload on firestore and view, show in another view via state variable etc etc to view the image, all show the same result
// Firestore upload manager code was here - removed it for the question
}
if let img = savedImage {
Image(uiImage: img)
.border(Color.red)
.background(.purple)
}
}
.navigationBarBackButtonHidden(true)
}
}
extension View {
func snapshot() -> UIImage {
let controller = UIHostingController(rootView: self)
let view = controller.view
let targetSize = controller.view.intrinsicContentSize
view?.bounds = CGRect(origin: .zero, size: targetSize)
view?.backgroundColor = .clear
let renderer = UIGraphicsImageRenderer(size: targetSize)
return renderer.image { _ in
view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
}
}
}
USDZModelView
is displayed from another view before it via .fullScreenCover
Here is a link to a sample .usdz file I found on the internet if any1 is interested in trying the code with the .usdz file in order to find a solution.
I resolved my issue by implementing the same functionality in Swift/UIKit. SCNView
in UIKit has a built in snapshot functionality that works perfectly. Implemented this functionality in a UIViewController
and integrated that in my SwiftUI app.