SwiftUI · · 4 min read

Using ImageRenderer to Convert SwiftUI Views into Images

Using ImageRenderer to Convert SwiftUI Views into Images

ImageRenderer is another new API for SwiftUI that comes with iOS 16. It allows you to easily convert any SwiftUI views into an image. The implementation is very simple. You instantiate an instance of ImageRenderer with a view for the conversion:

let renderer = ImageRenderer(content: theView)

You can then access the cgImage or uiImage property to retrieve the generated image.

As always, I love to demonstrate the usage of an API with an example. Earlier, we’ve built a line chart using the new Charts framework. Let’s see how to let users save the chart as an image in the photo album and share it using ShareLink.

Revisit the Chart View

swiftui-line-chart

First, let’s revisit the code of the ChartView example. We used the new API of the Charts framework to create a line chart and display the weather data. Here is the code snippet:

var body: some View {
    VStack {
        Chart {
            ForEach(chartData, id: \.city) { series in
                ForEach(series.data) { item in
                    LineMark(
                        x: .value("Month", item.date),
                        y: .value("Temp", item.temperature)
                    )
                }
                .foregroundStyle(by: .value("City", series.city))
                .symbol(by: .value("City", series.city))
            }
        }
        .chartXAxis {
            AxisMarks(values: .stride(by: .month)) { value in
                AxisGridLine()
                AxisValueLabel(format: .dateTime.month(.defaultDigits))

            }
        }
        .chartPlotStyle { plotArea in
            plotArea
                .background(.blue.opacity(0.1))
        }
        .chartYAxis {
            AxisMarks(position: .leading)
        }
        .frame(width: 350, height: 300)
        .padding(.horizontal)

    }
}

To use ImageRenderer, we first refactor this piece of code into a separate view like this:

struct ChartView: View {
    let chartData = [ (city: "Hong Kong", data: hkWeatherData),
                      (city: "London", data: londonWeatherData),
                      (city: "Taipei", data: taipeiWeatherData)
    ]
    
    var body: some View {
        VStack {
            
            Chart {
                ForEach(chartData, id: \.city) { series in
                    ForEach(series.data) { item in
                        LineMark(
                            x: .value("Month", item.date),
                            y: .value("Temp", item.temperature)
                        )
                    }
                    .foregroundStyle(by: .value("City", series.city))
                    .symbol(by: .value("City", series.city))
                }
            }
            .chartXAxis {
                AxisMarks(values: .stride(by: .month)) { value in
                    AxisGridLine()
                    AxisValueLabel(format: .dateTime.month(.defaultDigits))
                    
                }

            }
            .chartPlotStyle { plotArea in
                plotArea
                    .background(.blue.opacity(0.1))
            }
            .chartYAxis {
                AxisMarks(position: .leading)
            }
            .frame(width: 350, height: 300)
            .padding(.horizontal)
            
        }
    }
}

Next, we declare a variable to hold the view:

var chartView = ChartView()

Converting the View into an Image using ImageRenderer

Now we are ready to convert the chart view into an image. We will add a button named Save to Photos for saving the chart view image in the photo album.

Let’s implement the button like this:

var body: some View {

    VStack(spacing: 20) {
        chartView

        HStack {
            Button {
                let renderer = ImageRenderer(content: chartView)

                if let image = renderer.uiImage {
                    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
                }
            } label: {
                Label("Save to Photos", systemImage: "photo")
            }
            .buttonStyle(.borderedProminent)
        }
    }

}

In the closure of the button, we create an instance of ImageRenderer with chartView and get the rendered image by using the uiImage property. Then we call UIImageWriteToSavedPhotosAlbum to save the image to the photo album.

Note: You need to add a key named Privacy – Photo Library Usage Description in the info.plist before the app can properly save an image to the built-in photo album.

Adding a Share Button

swiftui-share-imagerenderer

Earlier, you learned how to use ShareLink to present a share sheet for content sharing. With ImageRenderer, you can easily build a function for users to share the chart view.

For convenience purpose, let’s refactor the code for image rendering into a separate method:

@MainActor
private func generateSnapshot() -> UIImage {
    let renderer = ImageRenderer(content: chartView)

    return renderer.uiImage ?? UIImage()
}

The generateSnapshot method converts the chartView into an image.

Note: If you are new to @MainActor, you can check out this article.

With this helper method, we can create a ShareLink like this in the VStack view:

ShareLink(item: Image(uiImage: generateSnapshot()), preview: SharePreview("Weather Chart", image: Image(uiImage: generateSnapshot())))
.buttonStyle(.borderedProminent)

Now when you tap the Share button, the app captures the line chart and lets you share it as an image.

swiftui-weather-chart-imagerenderer

Adjusting the Image Scale

You may notice the resolution of the rendered image is a bit low. The ImageRenderer class has a property named scale for you to adjust the scale of the rendered image. By default, its value is set to 1.0. To generate an image with a higher resolution, you can set it to 2.0 or 3.0. Alternatively, you can set the value to the scale of the screen:

renderer.scale = UIScreen.main.scale

Summary

The ImageRenderer class has made it very easy to convert any SwiftUI views into an image. If your app supports iOS 16 or up, you can use this new API to create some convenient features for your users. Other than rendering images, ImageRenderer also lets you render a PDF document. You can refer to the official documentation for further details.

For charts, Apple also comes with a more specific renderer called ChartRenderer for exporting a chart as an image. Later, we will further look into this class.

Read next