How I can convert a SwiftUI Image
to a UIImage
?
let image = Image(systemName: "circle.fill")
let UIImage = image as UIImage
How I can convert a SwiftUI Image
to a UIImage
?
let image = Image(systemName: "circle.fill")
let UIImage = image as UIImage
There is no direct way of converting an Image to UIImage. instead, you should treat the Image as a View, and try to convert that View to a UIImage. Image conforms to View, so we already have the View we need. now we just need to convert that View to a UIImage.
We need 2 components to achieve this. First, a function to change our Image/View to a UIView, and second one, to change the UIView we created to UIImage.
For more Convenience, both functions are declared as Extensions to their appropriate types.
extension View {
// This function changes our View to UIView, then calls another function
// to convert the newly-made UIView to a UIImage.
public func asUIImage() -> UIImage {
let controller = UIHostingController(rootView: self)
// Set the background to be transparent incase the image is a PNG, WebP or (Static) GIF
controller.view.backgroundColor = .clear
controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)
let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
controller.view.bounds = CGRect(origin: .zero, size: size)
controller.view.sizeToFit()
// here is the call to the function that converts UIView to UIImage: `.asUIImage()`
let image = controller.view.asUIImage()
controller.view.removeFromSuperview()
return image
}
}
extension UIView {
// This is the function to convert UIView to UIImage
public func asUIImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
How To Use?
let image: Image = Image("MyImageName") // Create an Image anyhow you want
let uiImage: UIImage = image.asUIImage() // Works Perfectly
Bonus
As i said, we are treating the Image, as a View. In the process, we don't use any specific features of Image, the only thing that is important is that our Image is a View (conforms to View protocol). This means that with this method, you can not only convert an Image to a UIImage, but also you can convert any View to a UIImage.
var myView: some View {
// create the view here
}
let uiImage = myView.asUIImage() // Works Perfectly
Such thing is not possible with SwiftUI, and I bet it will never be. It goes against the whole framework concept. However, you can go the opposite direction:
let uiImage = UIImage(systemName: "circle.fill")
let image = Image(uiImage: uiImage)
Try to add extension on View like so:
extension Image {
@MainActor
func getUIImage(newSize: CGSize) -> UIImage? {
let image = resizable()
.scaledToFill()
.frame(width: newSize.width, height: newSize.height)
.clipped()
return ImageRenderer(content: image).uiImage
}
}
And your use case will be
let image = Image("someFromAssets")
let size = CGSize(width: 100, height: 100)
let uiImage = image.getUIImage(newSize: size)
As of iOS 16 and MacOS 13, most answers above are out of date. The answer now is to use ImageRenderer, which is designed to transform Views -- including Image -- to both UIImage and CGImage.
The main caveat is that ImageRenderer wants to run in the main thread, so you'll need to use GCD to get the image converted.
public func convert(image: Image, callback: @escaping ((UIImage?) -> Void)) {
DispatchQueue.main.async {
let renderer = ImageRenderer(content: image)
// to adjust the size, you can use this (or set a frame to get precise output size)
// renderer.scale = 0.25
// for CGImage use renderer.cgImage
callback(renderer.uiImage)
}
}
I have been using a wrapper as a workaround to achieve this
struct AppImage: View {
private let name: String
var body: some View {
Image(name)
}
var asUIImage: UIImage? {
UIImage(named: name)
}
}
If you want to download an image from the internet, you can use Kingfisher to achieve the same output.
import UIKit
import Kingfisher
struct AppImage: View {
private let urlString: String
var body: some View {
KFImage(URL(string: urlString))
}
var asUIImage: UIImage {
let imageView = UIImageView()
imageView.kf.setImage(with: URL(string: urlString))
return imageView.image ?? UIImage(named: "placeholder-image")!
}
}
Later on you can use any Image in your app as a UIImage
if you stick to the AppImage
type when initialising your images:
AppImage(name: "image-name").asUIImage
or
AppImage(urlString: "https://url-to-your-image.com").asUIImage