drawHierarchy blocking main thread, any way to do this in the background?

Viewed 21

I have the following View extension function which creates a UIImage of the SwiftUI view by putting it in a UIHostingController then using a UIGraphicsImageRenderer to render the view:

extension View {
  func snapshot() -> UIImage {
    let controller = UIHostingController(rootView: self.ignoresSafeArea())
    let view = controller.view

    let targetSize = controller.view.intrinsicContentSize
    view?.bounds = CGRect(origin: .zero, size: targetSize)
    view?.backgroundColor = .clear

    let format = UIGraphicsImageRendererFormat()
    format.scale = 1

    let renderer = UIGraphicsImageRenderer(size: targetSize, format: format)

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

This works but it blocks the main thread completely such that any loading spinners I display on screen don't spin. How can I stop that, or is there another way of creating a snapshot that I could use that doesn't block the main thread? Thanks!

1 Answers

You can't run UI on background, but as help states about drawHierarchy:

Use this method when you want to apply a graphical effect, such as a blur, to a view snapshot. This method is not as fast as the snapshotView(afterScreenUpdates:) method.

Since you are not applying any graphical effect, you could use snapshotView, which as help again states:

This method very efficiently captures the current rendered appearance of a view and uses it to build a new snapshot view. You can use the returned view as a visual stand-in for the current view in your app. For example, you might use a snapshot view for animations where updating a large view hierarchy might be expensive. Because the content is captured from the already rendered content, this method reflects the current visual appearance of the view and is not updated to reflect animations that are scheduled or in progress. However, calling this method is faster than trying to render the contents of the current view into a bitmap image yourself.

Related