How to convert a View (not UIView) to an image?

Although SwiftUI does not provide a direct method to convert a view into an image, you still can do it. It is a little bit of a hack, but it works just fine.

In the example below, the code captures the image of two VStacks whenever they are tapped. Their contents are converted into a UIImage (that you can later save to a file if you need). In this case, I am just displaying it below.

Note that the code can be improved, but it provides the basics to get you started. I use GeometryReader to get the coordinates of the VStack to capture, but it could be improved with Preferences to make it more robust. Check the links provided, if you need to learn more about it.

Also, in order to convert an area of the screen to an image, we do need a UIView. The code uses UIApplication.shared.windows[0].rootViewController.view to get the top view, but depending on your scenario you may need to get it from somewhere else.

Good luck!

enter image description here

And this is the code (tested on iPhone Xr simulator, Xcode 11 beta 4):

import SwiftUI

extension UIView {
    func asImage(rect: CGRect) -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: rect)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

struct ContentView: View {
    @State private var rect1: CGRect = .zero
    @State private var rect2: CGRect = .zero
    @State private var uiimage: UIImage? = nil

    var body: some View {
        VStack {
            HStack {
                VStack {
                    Text("LEFT")
                    Text("VIEW")
                }
                .padding(20)
                .background(Color.green)
                .border(Color.blue, width: 5)
                .background(RectGetter(rect: $rect1))
                .onTapGesture { self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect1) }

                VStack {
                    Text("RIGHT")
                    Text("VIEW")
                }
                .padding(40)
                .background(Color.yellow)
                .border(Color.green, width: 5)
                .background(RectGetter(rect: $rect2))
                .onTapGesture { self.uiimage = UIApplication.shared.windows[0].rootViewController?.view.asImage(rect: self.rect2) }

            }

            if uiimage != nil {
                VStack {
                    Text("Captured Image")
                    Image(uiImage: self.uiimage!).padding(20).border(Color.black)
                }.padding(20)
            }

        }

    }
}

struct RectGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { proxy in
            self.createView(proxy: proxy)
        }
    }

    func createView(proxy: GeometryProxy) -> some View {
        DispatchQueue.main.async {
            self.rect = proxy.frame(in: .global)
        }

        return Rectangle().fill(Color.clear)
    }
}

Leave a Comment