TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Resizing images to fit the available space

Paul Hudson    @twostraws   

When we create an Image view in SwiftUI, it will automatically size itself according to the dimensions of its contents. So, if the picture is 1000x500, the Image view will also be 1000x500. This is sometimes what you want, but mostly you’ll want to show the image at a lower size, and I want to show you how that can be done, but also how we can make an image fit some amount of the user’s screen width using a relative frame.

First, add some sort of image to your project. It doesn’t matter what it is, as long as it’s wider than the screen. I called mine “Example”, but obviously you should substitute your image name in the code below.

Now let’s draw that image on the screen:

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

Tip: When you're using fixed image names such as this one, Xcode generates constant names for them all that you can use in place of strings. In this case, that means writing Image(.example), which is much safer than using a string!

Even in the preview you can see that’s way too big for the available space. Images have the same frame() modifier as other views, so you might try to scale it down like this:

Image(.example)
    .frame(width: 300, height: 300)

However, that won’t work – your image will still appear to be its full size. If you want to know why, change Xcode's preview mode from Live to Selectable – look for the three buttons at the bottom left of your Xcode preview, and click the one with a mouse cursor inside.

Important: This stops your preview from running live, so you won't be able to interact with your view until you select the Live option instead.

With Selectable mode enabled, take a close look at the preview window: you’ll see your image is full size, but there’s now a box that’s 300x300, sat in the middle. The image view’s frame has been set correctly, but the content of the image is still shown as its original size.

Try changing the image to this:

Image(.example)
    .frame(width: 300, height: 300)
    .clipped()

Now you’ll see things more clearly: our image view is indeed 300x300, but that’s not really what we wanted.

If you want the image contents to be resized too, we need to use the resizable() modifier like this:

Image(.example)
    .resizable()
    .frame(width: 300, height: 300)

That’s better, but only just. Yes, the image is now being resized correctly, but it’s probably looking squashed. My image was not square, so it looks distorted now that it’s been resized into a square shape.

To fix this we need to ask the image to resize itself proportionally, which can be done using the scaledToFit() and scaledToFill() modifiers. The first of these means the entire image will fit inside the container even if that means leaving some parts of the view empty, and the second means the view will have no empty parts even if that means some of our image lies outside the container.

Try them both to see the difference for yourself. Here is .fit mode applied:

Image(.example)
    .resizable()
    .scaledToFit()
    .frame(width: 300, height: 300)

And here is scaledToFill():

Image(.example)
    .resizable()
    .scaledToFill()
    .frame(width: 300, height: 300)

All this works great if we want fixed-sized images, but very often you want images that automatically scale up to fill more of the screen in one or both dimensions. That is, rather than hard-coding a width of 300, what you really want to say is “make this image fill 80% of the width of the screen.”

Rather than forcing a specific frame, SwiftUI has a dedicated containerRelativeFrame() modifier that lets us get exactly the result we want. The "container" part might be the whole screen, but it might also just be the part of the screen that this view's immediate parent occupies – maybe our image is shown inside a VStack along with other views.

We’ll go into much more detail on container relative frames in project 18, but for now we’re going to use it for one job: to make sure our image fills 80% of the available width of our screen.

For example, we could make an image that’s 80% the width of the screen:

Image(.example)
    .resizable()
    .scaledToFit()
    .containerRelativeFrame(.horizontal) { size, axis in
        size * 0.8
    }

Let's break that code down:

  1. We're saying we want to give this image a frame relative to the horizontal size of its parent. We aren't specifying a vertical size; more on that in a moment.
  2. SwiftUI then runs a closure where we're given a size and an axis. For us the axis will be .horizontal because that's the one we're using, but this matters more when you create relative horizontal and vertical sizes. The size value will be the size of our container, which for this image is the full screen.
  3. We need to return the size we want for this axis, so we're sending back 80% of the container's width.

Again, we don't need to specify a height here. This is because we’ve given SwiftUI enough information that it can automatically figure out the height: it knows the original width, it knows our target width, and it knows our content mode, so it understands how the target height of the image will be proportional to the target width.

Hacking with Swift is sponsored by Superwall.

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

Learn More

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.7/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.