UIButton
iOS Development
Image Scaling
AspectFit
Swift Programming

scale Image in an UIButton to AspectFit?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When a button shows an image, the most common problem is not loading the image but fitting it correctly inside the button bounds. On iOS, UIButton uses an internal imageView, and you usually get the desired layout by combining edge insets with the right content mode instead of resizing the bitmap yourself.

How UIButton Lays Out Images

A UIButton is a UIControl that manages a title label and an image view. The image is drawn inside button.imageView, and that inner view has its own contentMode. If you only set the button frame and do nothing else, UIKit may center the image at its natural size, which is why large assets can look cropped or small assets can appear misaligned.

For an aspect-fit result, set the image view content mode to scaleAspectFit and make sure the button is not forcing extra padding that works against the image size.

swift
1import UIKit
2
3final class ViewController: UIViewController {
4    override func viewDidLoad() {
5        super.viewDidLoad()
6
7        let button = UIButton(type: .system)
8        button.frame = CGRect(x: 40, y: 120, width: 160, height: 100)
9        button.backgroundColor = .secondarySystemBackground
10        button.setImage(UIImage(named: "photo"), for: .normal)
11
12        button.imageView?.contentMode = .scaleAspectFit
13        button.contentHorizontalAlignment = .fill
14        button.contentVerticalAlignment = .fill
15        button.imageEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
16
17        view.addSubview(button)
18    }
19}

This works because the button fills its content area, while the image view preserves the image ratio inside that area.

A Reliable Configuration Pattern

There are two layout questions to answer:

  1. How much of the button should be available to the image?
  2. Should the image stretch, fill, or preserve its aspect ratio?

For icons and photos, preserving the ratio is usually correct. A common pattern is to give the image some padding and let the image view scale within the padded region.

swift
1let button = UIButton(type: .custom)
2button.translatesAutoresizingMaskIntoConstraints = false
3button.setImage(UIImage(named: "avatar"), for: .normal)
4button.imageView?.contentMode = .scaleAspectFit
5button.contentHorizontalAlignment = .fill
6button.contentVerticalAlignment = .fill
7button.imageEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)

If you use Auto Layout, the button size still comes from constraints, not from the image. That makes the behavior predictable across device sizes.

Using Modern Button Configuration

On newer iOS versions, UIButton.Configuration gives you a cleaner API for spacing and padding. The aspect-fit part still belongs to the image view, but the configuration object makes the outer layout easier to reason about.

swift
1import UIKit
2
3let button = UIButton(configuration: .plain())
4button.configuration?.image = UIImage(systemName: "star.fill")
5button.configuration?.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
6button.tintColor = .systemOrange
7button.imageView?.contentMode = .scaleAspectFit

This is especially useful when the button has both text and an icon. You can control padding with the configuration instead of mixing title and image edge inset logic.

When to Resize the Asset Instead

Aspect fit is the right choice when the source image can vary or when you want one asset to work across multiple button sizes. Pre-resizing the image is only useful when you need exact pixel control or want to reduce memory usage for very large source files.

If your image is several thousand pixels wide and the button is small, loading the full bitmap can be wasteful. In that case, generate a smaller image once and still display it with aspect fit.

swift
1func resizedImage(_ image: UIImage, targetSize: CGSize) -> UIImage {
2    let renderer = UIGraphicsImageRenderer(size: targetSize)
3    return renderer.image { _ in
4        image.draw(in: CGRect(origin: .zero, size: targetSize))
5    }
6}

Use this only for performance or asset-pipeline reasons. It is not the first tool for layout problems.

Common Pitfalls

The first common mistake is setting button.contentMode = .scaleAspectFit and expecting it to affect the inner image. In practice, you usually need button.imageView?.contentMode = .scaleAspectFit because the image is rendered by the nested image view.

Another mistake is leaving the default content alignment in place. If horizontal and vertical alignment are not set to fill, the image view may not expand to use the full content rect, so aspect fit has less room to work with.

A third issue is mixing old edge inset logic with constraints without checking the final tappable area. If the insets are too large, the image becomes tiny even though the button itself is correctly sized.

Finally, avoid solving this by distorting the original asset. scaleToFill can make logos and photos look wrong. If the goal is to preserve the image ratio, stay with aspect fit and adjust the content area instead.

Summary

  • Use button.imageView?.contentMode = .scaleAspectFit for the image itself.
  • Set content alignment to fill so the image view can use the button bounds.
  • Add padding with imageEdgeInsets or configuration?.contentInsets.
  • Use Auto Layout to size the button, not the image asset.
  • Resize the bitmap only when you need performance or exact pixel control.

Course illustration
Course illustration

All Rights Reserved.