How to resize a SwiftUI Image and keep its aspect ratio

⋅ 3 min read ⋅ SwiftUI Image Resize

Table of Contents

By default, SwiftUI's Image views automatically size themselves to their image contents. If the image is larger than the device screen, it will go beyond it.

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

The image will render at its actual size, which is larger than the device, so you will only see a portion of it.[1]

The actual size of the image is 4016x6016 which is larger than the device 844x390 (landscape).
The actual size of the image is 4016x6016 which is larger than the device 844x390 (landscape).

Resizable

To make an image scales to fit the current view, we use the resizable() modifier, which resizes an image to fit available space.

struct ContentView: View {
var body: some View {
Image("donuts")
.resizable()
}
}

We can see the whole image now, but it got stretched out and losing its aspect ratio, which is usually not the behavior we want.

The image view resize to fit available space.
The image view resize to fit available space.

Aspect ratio

We use .aspectRatio(_:contentMode:) modifier to constraints the view dimensions. The method accept one of two strategies of ContentMode, fit and fill.

Fit

Fit content mode attempts to show the whole image by scales the image to fit the view size along one axis, possibly leaving empty space along the other axis. The result would guarantee that the whole image is shown.

struct ContentView: View {
var body: some View {
Image("donuts")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
Aspect fit
Aspect fit.

Fill

Fill content mode attempts to fill the image to available space by scales the image to fill the entire view leaving no empty space in any axis. This leads to a potential of an image go beyond the view's bounds.

struct ContentView: View {
var body: some View {
Image("donuts")
.resizable()
.aspectRatio(contentMode: .fill)
}
}
Aspect fill.
Aspect fill.

One thing you need to know, which is not obvious from our example, is that the fill content mode can lead to a potential of an image go beyond the view's bounds. And the image portion that goes beyond the image view won't get clipped.

To demonstrate this, I frame the image view to 200x200 and set fill content mode. I also add a pink border to make it easier to visualize the problem.

struct ContentView: View {
var body: some View {
Image("donuts")
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.border(Color.pink)
}
}

As you can see, the image fills within an image view frame, but the extending part doesn't get clipped. We have to clip it manually.

A portion of an image may extend beyond the view’s bounds in the fill content mode.
A portion of an image may extend beyond the view’s bounds in the fill content mode.

Clipping

To clip the image content within its view's bounds, we use clipped().

struct ContentView: View {
var body: some View {
Image("donuts")
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.border(Color.pink)
.clipped()
}
}
`.clipped()` modifier cuts off excess image part that goes beyond bounding frame.
`.clipped()` modifier cuts off excess image part that goes beyond bounding frame.

  1. Photo by Lore Schodts on Unsplash ↩︎


Read more article about SwiftUI, Image, Resize, or see all available topic

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron Tweet Share
Previous
Tuist scaffold: How to use the Tuist template to create a new module for an ongoing project

Learn how the scaffold command helps you to bootstrap new components or features such as a new VIPER module or a new framework for your new feature.

Next
What is the difference between Tuist init and scaffold

A brief summary of init and scaffold commands.

← Home