Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

SwiftUI image capturing View breaks EnvironmentObjects?

I am attempting to capture a screenshot of my view in SwiftUI. I have tried with both the ImageRenderer(content: myview) and with the below snapshot View extension. On both cases it crashes giving the error . . .

Fatal error: No ObservableObject of type isActive found. A
View.environmentObject(_:) for isActive may be missing as
an ancestor of this view.

I have tried both an empty environment object and the object with variables and it always get the same error. Is there any way to allow the use of environment objects when programmatically capturing a screenshot?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

//main view
struct TestView111: View {    
    var body: some View {
        VStack{
            otherview
            //click this to capture screenshot and break on environment var
            Button(action: {
                let image = otherview.snapshot()
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
            }, label: {
                Text("Save").buttonStyleBlue()
            })
        }
    }
    //view to take snapshot of
    var otherview: some View {
        TestView112()
    }
}

//sub view
struct TestView112: View {
    
    @EnvironmentObject private var objRect: GraphObjectRectList
    
    var body: some View {
        //can no longer find isActive here and breaks on btn click
        ForEach(objRect.isActive.indices, id: \.self) { i in
            Text(String(i))
        }
    }
}

//extension to take snapshot
extension View {
    func snapshot() -> UIImage {
        let controller = UIHostingController(rootView: self)
        let view = controller.view
        let size = CGSize(width: 500, height: 500)
        view?.bounds = CGRect(origin: .zero, size: size)
        view?.backgroundColor = .clear

        let renderer = UIGraphicsImageRenderer(size: size)

        return renderer.image { _ in
            view?.drawHierarchy(in: controller.view.bounds, afterScreenUpdates: true)
        }
    }
}

//my Environment class
class GraphObjectRectList: ObservableObject {
    @Published var isActive: [Bool] = [true, true]
}

//Scene View
struct TestAppApp: App {
    var body: some Scene {
        WindowGroup {
            TestView111()
                .environmentObject(GraphObjectRectList())
        }
    }
}

Is the graphics rendering engine is unable to access the global object twice?

Thanks for any help!

>Solution :

In your current example, the EnvironmentObject doesn’t exist on otherview because otherview doesn’t exist in the view hierarchy — it exists on its own in the Button‘s action.

To solve this, inject it on the version you’re sending to snapshot:

struct TestView111: View {
    @EnvironmentObject private var objRect: GraphObjectRectList //<-- Here

    var body: some View {
        VStack{
            otherview //<-- This one has a reference to the object, since it's in the view hierarchy
            Button(action: {
                let image = otherview.environmentObject(objRect).snapshot() //<-- Here
                UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
            }, label: {
                Text("Save")
            })
        }
    }
    //view to take snapshot of
    var otherview: some View {
        TestView112()
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading