Skip to content

Instantly share code, notes, and snippets.

@erica
Created June 20, 2019 23:25
Show Gist options
  • Select an option

  • Save erica/fb5005f625207e108836175ff201d8f2 to your computer and use it in GitHub Desktop.

Select an option

Save erica/fb5005f625207e108836175ff201d8f2 to your computer and use it in GitHub Desktop.
import PlaygroundSupport
import SwiftUI
extension UIView {
var renderedImage: UIImage {
let image = UIGraphicsImageRenderer(size: self.bounds.size).image { context in
UIColor.lightGray.set(); UIRectFill(bounds)
context.cgContext.setAlpha(0.75)
self.layer.render(in: context.cgContext)
}
return image
}
}
extension View {
var renderedImage: UIImage {
let window = UIWindow(frame: CGRect(origin: .zero, size: CGSize(width: 320, height: 160)))
let hosting = UIHostingController(rootView: self)
hosting.view.frame = window.frame
window.rootViewController = hosting
window.makeKey()
return hosting.view.renderedImage
}
}
Text("Hello").renderedImage
Slider(value: .constant(0.5)).renderedImage
let img = ([Color.red, .orange, .yellow, .green, .blue, .purple]
.reduce(AnyView(Text("👭").font(.largeTitle)
.rotationEffect(Angle(radians: .pi)))) {
AnyView($0.padding()
.background($1)
.rotationEffect(Angle(radians: .pi / 6)))
}).renderedImage
img
@alexbbrown
Copy link

alexbbrown commented Jun 21, 2019

Well, on Catalina I can do this. I haven't tried Mojave.

let view = ([Color.red, .orange, .yellow, .green, .blue, .purple]
    .reduce(AnyView(Text(":-)").font(.largeTitle)
        .rotationEffect(Angle(radians: .pi)))) {
            AnyView($0.padding()
                .background($1)
                .rotationEffect(Angle(radians: .pi / 6)))
})

PlaygroundPage.current.liveView = UIHostingController(rootView: view)

@alexbbrown
Copy link

but I see you already know how to do that: https://ericasadun.com/2019/06/06/good-things-swiftui-on-mojave-in-ios-playgrounds/

But on the other hand I got to execute some swiftUI for the first time. Thanks!

Copy link

ghost commented Aug 17, 2019

(You can’t conform View and if you conform the individual types, it doesn’t work.)

Maybe it's because it's a later beta, but for me this code works perfectly fine in Mojave:
Live Preview for SwiftUI in Mojave!

@snyeah
Copy link

snyeah commented Dec 1, 2019

I'm trying to render a SwiftUI View into a UIImage. This code works perfectly in Swift Playground. But in a simulator or real device, it generates a blank image. Any idea how could make it work please?

This is the code I'm using

let size = // Calcuate size //
let hosting = UIHostingController(rootView: self)
hosting.view.frame = CGRect(origin: .zero, size: size)
let image = hosting.view.renderedImage

@erica
Copy link
Author

erica commented Dec 6, 2019 via email

@snyeah
Copy link

snyeah commented Dec 6, 2019

I have a “report card” on screen which is a SwiftUI view. I want to use the export button to convert it to a UIImage then save to camera roll and / or send out the jpeg via email. Does SwiftUI view has a layer property or similar thing that I can work with CGContext?

@pakLebah
Copy link

I'm using XCode 11.3 on Mojave. None of these work. Could you please update the code? Thank you.

@erica
Copy link
Author

erica commented Aug 18, 2020

Leaving myself a note:

import PlaygroundSupport
import SwiftUI
extension UIView {
    var renderedImage: UIImage {
      let image = UIGraphicsImageRenderer(size: bounds.size).image { context in
        layer.render(in: context.cgContext)
      }
      return image
    }

    var renderedPlaygroundImage: UIImage {
    let image = UIGraphicsImageRenderer(size: bounds.size).image { context in
      UIColor.lightGray.set(); UIRectFill(bounds)
      context.cgContext.setAlpha(0.75)
      self.layer.render(in: context.cgContext)
    }
    return image
  }
}
extension View {
  var renderedImage: UIImage {
    let window = UIWindow(frame: CGRect(origin: .zero, size: CGSize(width: 320, height: 160)))
    let hosting = UIHostingController(rootView: self)
    hosting.view.frame = window.frame
    window.rootViewController = hosting
    window.makeKey()
    return hosting.view.renderedImage
  }
}

Text("Hello").renderedImage

Slider(value: .constant(0.5)).renderedImage
let img = ([Color.red, .orange, .yellow, .green, .blue, .purple]
  .reduce(AnyView(Text("👭").font(.largeTitle)
    .rotationEffect(Angle(radians: .pi)))) {
      AnyView($0.padding()
        .background($1)
        .rotationEffect(Angle(radians: .pi / 6)))
}).renderedImage
img

@scrappyTURTLEcom
Copy link

scrappyTURTLEcom commented Dec 6, 2020

If you add any effects to the views, such as .blur(radius: 10), layer.render(in: context.cgContext) will not render it to the image.

If you replace this:
layer.render(in: context.cgContext)
with this:
self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)

then all effects are included in the image... BUT then a new issue comes up. If you are dealing with high res images, for some reason the maximum size that can be drawn by self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true) is CGSize(width: 2730, height: 2730). if you increase the image size to 2731 or higher, the image will not be drawn.

1: Since layer.render(in: context.cgContext) has no size limitation, would like to know if there is another way to use it with .blur and other effects.
2: or how to over come the size limitation of self.drawHierarchy(in: self.layer.bounds, afterScreenUpdates: true)

I have posted my code at: https://stackoverflow.com/questions/65163097/convert-swiftui-view-to-uiimage

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment