- Published on
> How to Resize an Image in SwiftUI
- Authors

- Name
- Mick MacCallum
- @0x7fs
Images in SwiftUI display at their original size by default. To resize them, you need to make the image resizable first, then apply sizing modifiers. The order of modifiers matters—getting it wrong results in images that won't resize at all or that stretch in unexpected ways.
Making an Image Resizable
By default, SwiftUI images ignore frame modifiers:
// This won't resize the image
Image("photo")
.frame(width: 100, height: 100)
The image still displays at its intrinsic size. To enable resizing, add the .resizable() modifier:
Image("photo")
.resizable()
.frame(width: 100, height: 100)
Now the image fills the 100x100 frame, though it may appear stretched if the aspect ratio doesn't match.
Preserving Aspect Ratio
To resize while maintaining proportions, combine .resizable() with .scaledToFit() or .scaledToFill():
// Fits entirely within the frame, may leave empty space
Image("photo")
.resizable()
.scaledToFit()
.frame(width: 200, height: 200)
// Fills the frame completely, may crop edges
Image("photo")
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
With scaledToFit, the entire image is visible but may not fill the frame. With scaledToFill, the frame is completely filled but parts of the image may extend beyond the frame bounds.
Clipping Overflow
When using scaledToFill, the image often extends beyond its frame. Add .clipped() to cut off the overflow:
Image("photo")
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.clipped()
For rounded corners, use .clipShape() instead:
Image("photo")
.resizable()
.scaledToFill()
.frame(width: 200, height: 200)
.clipShape(RoundedRectangle(cornerRadius: 12))
aspectRatio Modifier
For more control, use the .aspectRatio() modifier with a content mode:
// Same as scaledToFit
Image("photo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
// Same as scaledToFill
Image("photo")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 200, height: 200)
You can also specify a custom aspect ratio:
// Force a 16:9 aspect ratio
Image("photo")
.resizable()
.aspectRatio(16/9, contentMode: .fit)
.frame(maxWidth: .infinity)
Flexible Sizing
Instead of fixed dimensions, let images flex based on available space:
Image("photo")
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity) // Full width, height adjusts
Or set a maximum size while allowing smaller:
Image("photo")
.resizable()
.scaledToFit()
.frame(maxWidth: 300, maxHeight: 200)
SF Symbols
System symbols work differently—they scale based on font size rather than frame:
// Scale with font
Image(systemName: "star.fill")
.font(.system(size: 50))
// Or use imageScale
Image(systemName: "star.fill")
.imageScale(.large)
If you need precise control over symbol size, you can make them resizable:
Image(systemName: "star.fill")
.resizable()
.frame(width: 44, height: 44)
AsyncImage Sizing
AsyncImage handles sizing slightly differently because the image might not exist yet:
AsyncImage(url: imageURL) { image in
image
.resizable()
.scaledToFill()
} placeholder: {
Rectangle()
.fill(Color.gray.opacity(0.3))
}
.frame(width: 200, height: 200)
.clipShape(RoundedRectangle(cornerRadius: 12))
Apply the frame to the AsyncImage container, and apply resizable() to the loaded image inside the closure.
Common Patterns
A profile avatar with a circular crop:
struct AvatarView: View {
let imageURL: URL?
var body: some View {
AsyncImage(url: imageURL) { image in
image
.resizable()
.scaledToFill()
} placeholder: {
Circle()
.fill(Color.gray.opacity(0.3))
}
.frame(width: 60, height: 60)
.clipShape(Circle())
}
}
A hero image that spans full width:
struct HeroImage: View {
let imageName: String
var body: some View {
Image(imageName)
.resizable()
.scaledToFill()
.frame(height: 250)
.frame(maxWidth: .infinity)
.clipped()
}
}
A thumbnail grid:
struct ThumbnailGrid: View {
let images: [String]
var body: some View {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 8) {
ForEach(images, id: \.self) { imageName in
Image(imageName)
.resizable()
.scaledToFill()
.frame(width: 100, height: 100)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
}
}
}
Interpolation Quality
For pixel art or images that shouldn't be smoothed when scaled, adjust the interpolation:
Image("pixel-art")
.resizable()
.interpolation(.none) // Preserves sharp pixel edges
.frame(width: 200, height: 200)
The default interpolation smooths edges when scaling, which is usually desirable for photos but ruins pixel art aesthetics.
Template Rendering
To tint an image with a color, use template rendering mode:
Image("icon")
.resizable()
.renderingMode(.template)
.frame(width: 44, height: 44)
.foregroundStyle(.blue)
This is useful for icons that need to match your app's color scheme.
Modifier Order Matters
The order of modifiers is critical with images. This works:
Image("photo")
.resizable() // 1. Make resizable
.scaledToFill() // 2. Set content mode
.frame(...) // 3. Apply size
.clipped() // 4. Clip overflow
This doesn't work as expected:
Image("photo")
.frame(...) // Frame has no effect yet
.resizable() // Now it's resizable but frame already applied
Always start with .resizable(), then content mode, then frame, then clipping.
// Continue_Learning
How to Round Specific Corners of a View in SwiftUI
Learn how to round only certain corners of a SwiftUI view using UnevenRoundedRectangle, custom shapes, and clipShape.
How to Change the Background Color of a View in SwiftUI
Learn different ways to set background colors on SwiftUI views, from simple color fills to gradients and materials.
Add Spacing to Toolbars with ToolbarSpacer in SwiftUI
Learn how to use ToolbarSpacer to add fixed and flexible spacing between toolbar items in SwiftUI, new in iOS 26.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.