Why this happens
In SwiftUI, an Image view defaults to displaying its asset at its exact intrinsic pixel or vector dimensions. When you apply a .frame(width:height:) modifier directly to an Image, SwiftUI constrains the layout view hierarchy to that frame, but it does not tell the underlying bitmap renderer to scale the graphic content.
As a result, the image renders at full size regardless of the frame dimensions you specify. If the image is larger than the frame, it overflows into surrounding views unless explicitly clipped, or it appears completely unaffected by width and height values.
SwiftUI decouples layout bounds from image transformation. To permit an image’s underlying pixels to scale up or down within its layout context, you must explicitly declare it as resizable by applying the .resizable() modifier before applying layout constraints.
How to fix it
1. Add the .resizable() modifier before .frame()
The most direct way to scale an image is to call .resizable() on the Image instance before declaring its frame dimensions. Modifier order matters in SwiftUI; calling .frame() before .resizable() will not scale the content properly.
import SwiftUI
struct SimpleResizedImageView: View {
var body: some View {
Image("thumbnailImage")
.resizable()
.frame(width: 32, height: 32)
}
}
By calling .resizable(), the image expands or shrinks to occupy whatever space the .frame() modifier allocates. However, if your frame dimensions have a different aspect ratio than the source asset, the image will stretch or compress unevenly.
2. Maintain aspect ratio with .scaledToFit() or .scaledToFill()
To resize an image without distorting its proportions, pair .resizable() with an aspect ratio modifier.
Use .scaledToFit() (equivalent to .aspectRatio(contentMode: .fit)) when the entire image must remain visible within the target frame boundaries:
import SwiftUI
struct FitImageView: View {
var body: some View {
Image("landscapePhoto")
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
}
}
Use .scaledToFill() (equivalent to .aspectRatio(contentMode: .fill)) when the image must completely cover the target frame dimensions. Because portions of the image may extend outside the frame boundaries, add .clipped() to trim any overflow:
import SwiftUI
struct FillImageView: View {
var body: some View {
Image("profileAvatar")
.resizable()
.scaledToFill()
.frame(width: 100, height: 100)
.clipped()
}
}
3. Use cap insets for stretchable assets
If you are scaling UI elements like chat bubbles, buttons, or border frames where corners must remain fixed while center areas stretch, pass EdgeInsets and a ResizingMode to .resizable():
import SwiftUI
struct StretchableBubbleView: View {
var body: some View {
Image("speechBubble")
.resizable(
capInsets: EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12),
resizingMode: .stretch
)
.frame(width: 240, height: 80)
}
}
You can set resizingMode to .stretch to stretch the inner content or .tile to repeat the inner pattern across the expanded frame.
4. Resizing SF Symbols
System symbols created with Image(systemName:) are vector graphics managed by typography rules. You have two ways to scale them:
Option A: Treat them like text (recommended for standard UI scaling)
Use the .font() modifier to change the symbol’s point size, or .imageScale() to adjust it relative to adjacent text:
Image(systemName: "star.fill")
.font(.system(size: 32))
Option B: Treat them like resizable bitmaps
If you need the symbol to fit an exact non-standard frame, you can still use .resizable():
Image(systemName: "star.fill")
.resizable()
.scaledToFit()
.frame(width: 32, height: 32)
How to prevent it
- Always place
.resizable()directly on theImageview before declaring.frame()or layout modifiers. - Pair
.resizable()with.scaledToFit()or.scaledToFill()by default to avoid accidental pixel distortion. - Use
.clipped()whenever you use.scaledToFill()inside a bounded.frame()to prevent visual bleed into adjacent views. - For
AsyncImage, configure the inner phase or content closure with.resizable(), because applying.resizable()toAsyncImagedirectly will cause a compiler error.
FAQ
How do I resize an image inside an AsyncImage view?
In an AsyncImage, apply .resizable() to the loaded Image passed into the content closure rather than the AsyncImage container itself. For example: AsyncImage(url: imageURL) { image in image.resizable().scaledToFit() } placeholder: { ProgressView() }.
Why does .frame() change the view size but not the picture inside it?
SwiftUI frames define the size of the layout container, not the scale of the bitmap inside it. Without .resizable(), the image renders at full intrinsic resolution and simply overflows or centers within the defined frame.
What is the difference between .scaledToFit() and .scaledToFill()?
The .scaledToFit() modifier ensures the entire image is visible within the given dimensions, potentially leaving empty space on the sides. The .scaledToFill() modifier ensures the image covers the entire frame, which can cause edges of the image to extend outside the frame boundaries unless clipped.
Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.
