BLACK FRIDAY: Save 50% on all our Swift books and bundles! >>

Buttons and images

Paul Hudson    @twostraws   

We’ve looked at SwiftUI’s buttons briefly previously, but they are remarkably flexible and can adapt to a huge range of use cases.

The simplest way to make a button is one we’ve looked at previously: when it just contains some text you pass in the title of the button, along with a closure that should be run when the button is tapped:

Button("Delete selection") {
    print("Now deleting…")
}

Of course, that could be any function rather than just a closure, so this kind of thing is fine:

struct ContentView: View {
    var body: some View {
        Button("Delete selection", action: executeDelete)
    }

    func executeDelete() {
        print("Now deleting…")
    }
}

There are few different ways we can customize the way buttons look. First, we can attach a role to the button, which iOS can use to adjust its appearance both visually and for screen readers. For example, we could say that our Delete button has a destructive role like this:

Button("Delete selection", role: .destructive, action: executeDelete)

Second, we can use one of the built-in styles for buttons: .bordered and .borderedProminent. These can be used by themselves, or in combination with a role:

VStack {
    Button("Button 1") { }
        .buttonStyle(.bordered)
    Button("Button 2", role: .destructive) { }
        .buttonStyle(.bordered)
    Button("Button 3") { }
        .buttonStyle(.borderedProminent)
    Button("Button 4", role: .destructive) { }
        .buttonStyle(.borderedProminent)
}

If you want to customize the colors used for a bordered button, use the tint() modifier like this:

Button("Button 3") { }
    .buttonStyle(.borderedProminent)
    .tint(.mint)

Important: Apple explicitly recommends against using too many prominent buttons, because when everything is prominent nothing is.

If you want something completely custom, you can pass a custom label using a second trailing closure:

Button {
    print("Button was tapped")
} label: {
    Text("Tap me!")
        .padding()
        .foregroundColor(.white)
        .background(.red)
}

This is particularly common when you want to incorporate images into your buttons.

SwiftUI has a dedicated Image type for handling pictures in your apps, and there are three main ways you will create them:

  • Image("pencil") will load an image called “Pencil” that you have added to your project.
  • Image(decorative: "pencil") will load the same image, but won’t read it out for users who have enabled the screen reader. This is useful for images that don’t convey additional important information.
  • Image(systemName: "pencil") will load the pencil icon that is built into iOS. This uses Apple’s SF Symbols icon collection, and you can search for icons you like – download Apple’s free SF Symbols app from the web to see the full set.

By default the screen reader will read your image name if it is enabled, so make sure you give your images clear names if you want to avoid confusing the user. Or, if they don’t actually add information that isn’t already elsewhere on the screen, use the Image(decorative:) initializer.

Because the longer form of buttons can have any kind of views inside them, you can use images like this:

Button {
    print("Edit button was tapped")
} label: { 
    Image(systemName: "pencil")
}

If you want both text and image at the same time, SwiftUI has a dedicated type called Label.

Button {
    print("Edit button was tapped")
} label: {
    Label("Edit", systemImage: "pencil")
}

That will show both a pencil icon and the word “Edit” side by side, which on the surface sounds exactly the same as what we’d get by using a simple HStack. However, SwiftUI is really smart: when we use a label it will automatically decide whether to show the icon, the text, or both depending on how they are being used in our layout. This makes Label a fantastic choice in many situations, as you’ll see.

Tip: If you find that your images have become filled in with a color, for example showing as solid blue rather than your actual picture, this is probably SwiftUI coloring them to show that they are tappable. To fix the problem, use the renderingMode(.original) modifier to force SwiftUI to show the original image rather than the recolored version.

Save 50% in my Black Friday sale.

SAVE 50% This Black Friday all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

BUY OUR BOOKS
Buy Pro Swift 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 (Vapor Edition) 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 Server-Side Swift (Kitura Edition) 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.