Skip to content

ImageRenderer Cannot Export UIKit-Backed Views

SwiftUI's ImageRenderer silently fails on views that use UIKit or AppKit under the hood, like List and ScrollView.

The Limitation

SwiftUI’s ImageRenderer lets you render any SwiftUI view into a UIImage or CGImage. It works well for pure SwiftUI views, but silently fails – producing a blank or incomplete image – when the view tree contains components backed by UIKit or AppKit. This includes several commonly used views:

  • List (wraps UITableView / NSTableView)

  • ScrollView (wraps UIScrollView / NSScrollView)

  • TextEditor (wraps UITextView / NSTextView)

  • Map (wraps MKMapView)

There is no compiler warning or runtime error. The renderer simply does not capture those portions of the view hierarchy.

The Workaround

When I needed to export a list-like layout as an image, the solution was to replace List with a pure SwiftUI equivalent built from VStack and manual styling:

let exportView = VStack(spacing: 0) {
   ForEach(items) { item in
      HStack {
         Text(item.name)
         Spacer()
         Text(item.value)
            .foregroundStyle(.secondary)
      }
      .padding(.horizontal, 16)
      .padding(.vertical, 12)

      if item.id != items.last?.id {
         Divider()
      }
   }
}
.background(.white)
.frame(width: 390)

let renderer = ImageRenderer(content: exportView)
renderer.scale = UIScreen.main.scale

if let image = renderer.uiImage {
   // Use the rendered image
}

The VStack with ForEach replicates the visual structure of a List without relying on any UIKit-backed views. Adding dividers, padding, and a background produces a result that looks close enough to a standard list for export purposes.

Practical Advice

If you plan to use ImageRenderer in your app, design your exportable views with this constraint in mind from the start. Build them from basic SwiftUI primitives: stacks, text, shapes, and images. Avoid any view that you know wraps a platform-specific control. Testing the render output early saves the frustration of discovering blank regions later.

Found this helpful? Follow me on Bluesky and Mastodon for more Swift tips and indie dev updates.