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
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
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.
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 ChartRendere
r for exporting a chart as an image. Later, we will further look into this class.