Mastering SwiftUI Image View

One of the key components of any mobile app is images. Images are used to convey information, add visual appeal, and enhance the user experience. In this blog post, we will explore how to work with images in SwiftUI. We will cover everything from adding images to a SwiftUI project, resizing and scaling images, and working with system images. You can also take advantage of the new AsyncImage to load images from a URL, which I am covering in a separate tutorial here.

By the end of this post, you will have a solid understanding of how to work with images and be able to create stunning and responsive user interfaces in your apps.

How to add an image to your Xcode project?

Before you can show an image in the app, you need to include it in your project. In most apps, you would typically download images from a server. Another use case is to ship the app directly with some images. For example, images that you want to show during the onboarding or icon images. You can add images to the asset catalog in Xcode. Please, pay attention to the size of these images, as they can add up quickly and significantly increase the app size during installation.

Here are the steps to add an image to the asset catalog:

  1. Open Xcode’s Asset Catalog by clicking on Assets.xcassets in the project navigator.
  2. Drag and drop your image into the asset list or Click on the plus button in the bottom left corner of the window and select “New Image Set”
  3. Name your new image set
Drag and drop an image into the assets file in Xcode.
Drag and drop an image into the assets file in Xcode.
Images have 3 different scales for different device screens. If you drag an image into assets, the image will only be used for the 1x scale.
Images have 3 different scales for different device screens. If you drag an image into assets, the image will only be used for the 1x scale.
A shortcut is to set the image scale to single. Then the image is used for all scale factors.
A shortcut is to set the image scale to single. Then the image is used for all scale factors.

In my example, I use a large image from Unsplash (Photo by Jametlene Reskp on Unsplash) with a resolution of 5472×3648 pixels. The file size is 3MB. Typically you would use something much smaller than that. But the image is a good example of how not to do it.

How do I display an image in SwiftUI?

To display an image in SwiftUI, you can use the Image view. You only need to add the image to the asset library of your Xcode project and then pass the image name to the Image() view. Here is an example of how to display an image in SwiftUI:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("puppies")
    }
}

#Preview {
    ContentView()
}
The puppies image is very large and fills up the entire screen of an iPhone 14.

In the image above only a small part of the puppies image is shown. To understand better why this is shown, let’s look at the same view on macOS.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("puppies")
            .overlay {
                Rectangle()
                    .stroke(Color.green, lineWidth: 50)
                    .frame(width: 1179 / 3,
                           height: 2556 / 3)
            }
    }
}
SwiftUI image on macOS shows a window that is large enough to fit the puppies image. The green area indicates the part of the image that is used for the iPhone 14 example from above.

SwiftUI image on macOS shows a window that is large enough to fit the puppies image. The green area indicates the part of the image that is used for the iPhone 14 example from above.

On macOS the full image is shown, because macOS uses windows that are scaling to fit their content. In order to show the size of the iPhone 14 screen area, I am using an overlay with a green rectangle. You can see that the image part corresponds to the iPhone image from above.

In order to get the right area, I had to consider the screen size and pixel density of the iPhone 14. An iPhone 14 Pro screen has a size of 1179 by 2556 pixels. The size of an image in SwiftUI is specified in point. All the newer screens are Retina and have a high pixel density. 1 point is equal to 3 pixel. The SwiftIU frame modifier uses points. I use the following conversion:

frame size(points) = iPhone screen size (pixel) // pixel per point

Which means for the iPhone 14 with a width of 1179 and a 3 pixel per point density, I have to set a frame modifier width of 1179/3 points.

Why do we have to deal with image scale factors?

In Xcode, you have to deal with image scale factors because of the different resolutions of devices that your app will run on. For example, if you have an image that is 100×100 pixels and you want it to look the same size on both an iPhone 8 and an iPhone 14, you would need to provide two versions of the image: one at 100×100 pixels for the iPhone 8 and one at 150×150 pixels for the iPhone 14. This is because the iPhone 14 has a higher resolution screen (3 pixel per point) than the iPhone 8 (2 pixel per point).

Technically you could use a smaller image also for the better screens, but the image will look more blurry. If you care to have sharp and good looking images, you should always provide higher resolution images for the newer devices.

Free SwiftUI Layout Book

Get Hands-on with SwiftUI Layout.

Master SwiftUI layouts with real-world examples and step-by-step guide. Building Complex and Responsive Interfaces.

How to work with different versions of your image instances?

As an example, I will use a simple gradient image with a size of 400 by 400 pixel. I created higher resolution versions for 2x with is 800 by 800 pixel and a 3x version with is 1200 by 1200 pixel. I dragged these images in the corresponding slots for the image asset. To better see which image asset is used when each image displays a text saying “1x”, “2x” or “3x”

Let’s now have a look at how this image is used for different devices. There are 2 environment properties that give you the pixel to point relationship: pixelLength and displayScale:

struct ContentView: View {

    @Environment(\.pixelLength) var pixelLength
    @Environment(\.displayScale) var displayScale
    
    var body: some View {
        VStack {
            Text("pixellength \(pixelLength)")
            Text("displayScale \(displayScale)")
            Image("test_image")
        }
        .font(.title)
    }
}
The iPhone 8 uses the 2x scale image. For the higher resolution iPhone 14, the 3x image is used. MacOS has a 2x scale.
The iPhone 8 uses the 2x scale image. For the higher resolution iPhone 14, the 3x image is used. MacOS has a 2x scale.

Using Vector based Assets

Vector graphics are computer images made using math equations that create lines and shapes. They are great for iOS apps because they can be scaled up or down without losing quality. This means that no matter how big or small the image is, it will always look clear and sharp. This is important for apps because they are used on different devices with varying screen sizes. Using vector graphics ensures that the app will look great on all devices.

Vector bases file formats are SVG, PDF, and EPS. Vector files are typically much smaller than bitmaps. The same image from above in jpeg with a size of 1200 by 1200 pixel is 651KB. Whereas a PDF version takes up only 8KB. If you want to use a smaller image type that is a bitmap use JPEG. PNG is quite large but allows for transparent backgrounds.

Customizing Vector Based Images with Rendering Mode

They’re a different rendering modes: original and template. In template mode, all non-transparent pixels will be rendered as the foreground color. This is a nice trick if you want to change the color of a graphics. 

import SwiftUI

struct TemplateIconTestView: View {

    var body: some View {
        Image("test_icon")
            .renderingMode(.template)
            .foregroundColor(.blue)
    }
}

#Preview {
    TemplateIconTestView()
}
The PDF file has an original color of black. By setting the rendering mode to template, I can set the foreground color to blue.

The PDF file has an original color of black. By setting the rendering mode to template, I can set the foreground color to blue.

Instead of specifying the render mode in code, you can change this in the assets setting. This is useful if you only use it as template.
Instead of specifying the render mode in code, you can change this in the assets setting. This is useful if you only use it as template.

Displaying System Symbol Image

SwiftUI provides a simple way to use system images in your app. System images are the icons that represent built-in features and functionalities of iOS and macOS, such as the battery icon, the Wi-Fi icon, and more. Using system images in your app can make it look more consistent with the rest of the system. You can find all available icons with the SF Symbols app.

To use a system image in SwiftUI, you can use the Image(systemName: “”) initializer. The systemName parameter takes the name of the system image you want to use. For example, to use the Wi-Fi icon, you can use Image(systemName: “wifi”).

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack(spacing: 20) {
            Image(systemName: "square.and.arrow.down.on.square")

            Image(systemName: "square.and.arrow.down.on.square")
               // .symbolRenderingMode(.monochrome)
                  .foregroundColor(.blue)

            Image(systemName: "square.and.arrow.down.on.square")
                .symbolRenderingMode(.hierarchical)
                .foregroundColor(.blue)

            Image(systemName: "square.and.arrow.down.on.square")
                //.symbolRenderingMode(.palette)
                .foregroundStyle(Color.pink, Color.green)
        }
        .font(.system(size: 70))
    }
}
Example of a system symbol image using the Image(systemName:) initializer in SwiftUI with different symbols rendering modes.

You can also customize the appearance of system images using the symbolRenderingMode() modifier. By default, system images are rendered using the monochrome mode, which means they only one color is used. In hierarchical mode some parts of the icon are semitransparent. To get a multicolour icon, you can use the foregroundStyle modifier and specify 2 colors.

To get a list of all the available system symbol images, you can check out the SF Symbols app. Download the app here.

System images are also used together with text for SwiftUI Label view.

Free SwiftUI Layout Book

Get Hands-on with SwiftUI Layout.

Master SwiftUI layouts with real-world examples and step-by-step guide. Building Complex and Responsive Interfaces.

Resizing and Scaling Images in SwiftUI

If you don’t want to use the original size of the image, you can easily resize images. SwiftUI provides several modifiers to help with this.

Using resizable() Modifier to Resize Images

The resizable() modifier allows us to resize an image to a specific width and height. We can use it as follows:

Image("puppies")
   .resizable()
   .frame(width: 100, height: 100)

This code resizes the image to a width and height of 100 points. If you don’t use the resizable modifier, the frame will have no effect.

Using aspectRatio() Modifier to Maintain Aspect Ratio

The aspectRatio() modifier allows us to maintain the aspect ratio of an image while resizing it. We can use it as follows:

Image("myImage")
   .resizable()
   .aspectRatio(contentMode: .fit)

This code maintains the aspect ratio of the image while fitting it within its parent view.

Using scaledToFit() and scaledToFill() Modifiers to Scale Images

The scaledToFit() and scaledToFill() modifiers allow us to scale an image to fit its parent view. We can use them as follows:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("puppies")
            .resizable()
            .scaledToFit()
    }
}
import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("puppies")
            .resizable()
            .scaledToFill()
    }
}
swiftui image scale to fill
import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("puppies")
            .resizable()
            .scaledToFill()
            .edgesIgnoringSafeArea(.all)
    }
}
swiftui image scale to fill with edges igore safe area

The scaledToFit() modifier scales the image to fit within its parent view while maintaining its aspect ratio. The scaledToFill() modifier scales the image to fill its entire parent view, potentially cropping the edges of the image.

How to resize and crop an image to a specific size?

This code example resizes and crops an image to a specific size of 300 by 300 pixels.

import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("puppies")
            .resizable()
            .scaledToFill()
            .frame(width: 300,
                   height: 300,
                   alignment: .leading)
            .clipped()
    }
}
swiftui cropped image

Here’s a breakdown of what each line of code does:

  • The .resizable() modifier makes the image resizable so that it can be scaled up or down as needed.
  • The .scaledToFill() modifier scales the image to fill the available space, which can cause some parts of the image to be cropped if the aspect ratio of the image doesn’t match the aspect ratio of the available space.
  • The .frame(width:height:alignment:) modifier sets the frame size of the image to 300 by 300 pixels, with the alignment set to .leading (which means the image will be aligned to the left edge of the available space).
  • The .clipped() modifier clips the image to the frame size, which means that any parts of the image that fall outside the frame will be cropped.

Overall, this code snippet resizes and crops the “puppies” image to a specific size of 300 by 300 pixels, while maintaining the aspect ratio of the image and aligning it to fill the left edge of the available space.

Conclusion

So, you made it to the end! Congrats! By now, you should have a pretty good understanding of how to work with images in SwiftUI. Remember, images play a crucial role in mobile app development, so it’s important to master them. With the techniques we covered in this post, you’ll be able to resize and scale images, work with system images, and use SF symbols in SwiftUI images. Keep practicing, experimenting, and exploring new ways to make your images look great in your apps. You got this!

FAQ

What is the difference between UIImage and image in SwiftUI?

UIImage is a class in UIKit framework used to represent an image in iOS. It provides methods to load, create, and manipulate images. On the other hand, image is a struct in SwiftUI used to represent an image in the user interface. It is a lightweight and easy-to-use alternative to UIImage, and it provides a declarative way to display images in the app.

Because UIImage is a very convenient class, you can use it also inside SwiftUI. Use the Image(uiimage) initialiser.

What image formats does SwiftUI support?

SwiftUI supports several image formats, including JPEG, PNG, HEIC, and more. It also supports vector-based images using PDF and SVG formats. Additionally, SwiftUI provides support for system images and SF Symbols, which are vector-based icons that can be easily customized and resized. Overall, SwiftUI’s image-handling capabilities are quite versatile and flexible, allowing you to work with a wide range of image formats and styles.

Further Reading:

Leave a Comment

Subscribe to My Newsletter

Want the latest iOS development trends and insights delivered to your inbox? Subscribe to our newsletter now!

Newsletter Form