iosswiftswiftuiuiimage

How to render images @2x and @3x from a URL in a SwiftUI Image


I have requirement to load an advertisement image in my app and these are usually logos of companies.

The thing about logos is that while they can be of a specific height, their width would change.

So I started with a very simple solution, I uploaded a 90 x 20 png image in Firebase storage and then used the simples code:

Image(uiImage: image) and this rendered the rendered the image at 90 x 20.

Some details left out is that I make a network request in the background to download this image and then - I feel this might be not relevant but can add code if needed.

The issue is that on an iPhone 16 (regular and pro) device, these image get really blurry.

So what I did was upload a 3x image but that previous code made the image grow massively as now the image dimensions were 270 x 60.

So to render the high quality image at the previous dimensions, I did the following:

Image(uiImage: image)
    .resizable()
    .renderingMode(.template)
    .scaledToFit()
    .frame(height: 20)

// The width of the parent view hosting this had a max width of infinity

Now this worked and the images were sharper. The issue with specifying a hardcoded height of 20, we ran into the issue with some company's logos being a bit taller so then we came up with a rule where the max height of the logo should be 28 so:

  1. If I upload 90 x 20, the images height would be 20
  2. If I uploaded 360 x 80, the image height would be 28

I achieve this via:

Image(uiImage: image)
    .resizable()
    .renderingMode(.template)
    .tint(Constants.Colors.logo)
    .scaledToFit()
    .frame(height: min(image.size.height, 28))

This works for most of the use cases, however, if I want an image to render at 90 x 20 but at a high quality, and if I upload an image at 3x 270 x 60, it would not render at 90 x 20 but rather at 126 x 28 because 28 is the max height

While this might be acceptable, what would be a good way to render the 2x and 3x images at its original dimension rather than making the image grow to the 28 max height.


Solution

  • You should create the UIImage using init(data:scale:). This initialiser allows you to specify the scale and it will be scaled accordingly when it is displayed.

    Alternatively, create a CGDataProvider using the Data you got, then create a CGImage from that (e.g. this initialiser for pngs). This CGImage allows you to access the width and height in pixels, which you can use to calculate the scale. Finally, create a SwiftUI Image using init(_:scale:orientation:label:), pass in the CGImage and the scale. The label is for accessibility.