Swift
UIButton
iOS Development
Image Update
Programming Tutorial

How to change UIButton image in Swift

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Changing a UIButton image in Swift is a common UI requirement for toggles, playback controls, favorites, and stateful actions. It looks simple, but reliable implementations need to handle control states (normal, highlighted, selected, disabled), asset sizing, rendering mode, and async updates.

If image updates are done ad hoc in multiple places, buttons can show the wrong icon after reuse, state transitions, or theme changes. A better approach is to map states clearly and update image + accessibility together.

Core Sections

1. Set images by control state

Use setImage(_:for:) to assign an icon for each relevant state.

swift
1import UIKit
2
3final class PlayerViewController: UIViewController {
4    @IBOutlet private weak var playPauseButton: UIButton!
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8
9        playPauseButton.setImage(UIImage(named: "icon-play"), for: .normal)
10        playPauseButton.setImage(UIImage(named: "icon-pause"), for: .selected)
11        playPauseButton.setImage(UIImage(named: "icon-play-disabled"), for: .disabled)
12    }
13
14    @IBAction private func onTapPlayPause(_ sender: UIButton) {
15        sender.isSelected.toggle()
16    }
17}

This keeps behavior predictable: toggling isSelected switches icon automatically.

2. Use SF Symbols and rendering configuration

For modern iOS, SF Symbols plus symbol configuration gives consistent visual scale.

swift
1let config = UIImage.SymbolConfiguration(pointSize: 20, weight: .semibold)
2let heart = UIImage(systemName: "heart", withConfiguration: config)
3let heartFill = UIImage(systemName: "heart.fill", withConfiguration: config)
4
5favoriteButton.setImage(heart, for: .normal)
6favoriteButton.setImage(heartFill, for: .selected)
7favoriteButton.tintColor = .systemRed

If your asset should keep original colors, set rendering mode:

swift
let image = UIImage(named: "brand-icon")?.withRenderingMode(.alwaysOriginal)
brandButton.setImage(image, for: .normal)

Without this, system tinting may recolor the icon unexpectedly.

3. Update images safely during async workflows

When icons depend on remote state, update UI on the main thread and avoid race conditions.

swift
1func refreshBookmarkState(itemId: String) {
2    Task {
3        let isBookmarked = await bookmarkService.fetchBookmarkState(itemId: itemId)
4        await MainActor.run {
5            bookmarkButton.isSelected = isBookmarked
6            bookmarkButton.accessibilityLabel = isBookmarked ? "Remove bookmark" : "Add bookmark"
7        }
8    }
9}

If your button appears inside reusable cells, reset state in prepareForReuse and rebind state during configuration to avoid stale images.

For iOS 15+, UIButton.Configuration can simplify consistent image/text spacing:

swift
1var cfg = UIButton.Configuration.plain()
2cfg.image = UIImage(systemName: "square.and.arrow.up")
3cfg.imagePadding = 6
4shareButton.configuration = cfg

Common Pitfalls

  • Setting only the .normal image and expecting automatic updates for selected or disabled states.
  • Updating button images from background threads after network calls.
  • Using template images unintentionally and getting unexpected tint colors.
  • Forgetting to synchronize isSelected with underlying model state, causing UI drift.
  • Not updating accessibility labels/hints when image meaning changes.

Summary

Changing a UIButton image in Swift is most reliable when you treat it as state mapping, not one-off assignment. Define icons per control state, use symbol or rendering configuration intentionally, and perform async UI updates on the main thread. Pair visual updates with accessibility text so behavior is clear for all users.

For consistent visual behavior, define button imagery through design tokens or configuration structs rather than scattering literal asset names. A small ButtonIconSet model can map normal/selected/disabled images and accessibility labels together. This keeps state handling centralized and avoids stale references when assets are renamed. It also helps teams support theming (light, dark, brand variants) without touching business logic.

When debugging image updates that "do nothing," inspect content insets, image edge insets, and Auto Layout constraints. Sometimes the image is set correctly, but clipping or layout compression hides it. In reusable views like table or collection cells, always reset button state during reuse and reapply model state during configure. That pattern eliminates most phantom UI-state bugs.

Automated UI tests that tap stateful buttons and assert resulting icons can catch regressions early, especially after design-system or asset updates.

If the same icon appears in many screens, wrap setup in a helper method so state-image mapping remains consistent and easier to update during redesigns.


Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.