Last Chance: RW Accelerator Bootcamps

Bootcamps start August 15th — Don't miss out!
Save 50% and go from novice coder to job-ready mobile developer in just 12 weeks.

Home iOS & Swift Tutorials

UIButton Configuration Tutorial: Getting Started

Learn how to give your buttons some style and color using the UIButton Configuration API.

Version

  • Swift 5.5, iOS 15, Xcode 13

Buttons are a staple of all apps, big and small.

In this tutorial, you’ll learn how to take your app’s buttons to the next level using the UIButton Configuration API introduced in iOS 15. This API provides a flexible way to declare the style of buttons and have that style change when the state changes.

More specifically, you’ll learn how to do the following with the UIButton Configuration API:

  • Style buttons with ease.
  • Add images and SF Symbols to buttons.
  • Show a spinner on a button.
  • Update a button’s configuration dynamically.

Read on to discover everything you need to know to start configuring buttons!

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

A quick refresher: In iOS 14, Apple introduced the UIAction API for UIButton, which removed the need for the target-action pattern. Here’s a brief example of how to use this API:

button.addAction(
  UIAction { _ in
    print("You tapped the button!")
  }
)

When tapping the button, the block will be called and the print statement will execute.

If you’ve used a completion handler-based API before, this new UIAction API should feel familiar.

Now you’ll learn how to use the Configuration API to take your button’s styles to the next level with ease.

Beginning Adoption of UIButton.Configuration

Build and run the starter project. Then tap on the person icon.

The sample app's sign-in page, with two buttons: Sign In, and Get Help. Both buttons have no styling other than the default blue tinted title.

This opens the SignInViewController view controller. As you can see, it looks quite bland. Fortunately, this is easy to fix with the new Configuration API!

At a high level, there are four primitive options for a button configuration. They come as four different functions on UIButton.Configuration that generate a configuration object based on that template. The four options are as follows:

  • .plain: A plain button with a transparent background.
  • .filled: A button whose background is filled with a color.
  • .gray: A button with a gray background.
  • .tinted: A button whose background is tinted with a color.

Styling Options for Buttons

To start styling buttons, open SignInViewController.swift, and find signInButton.

At the top, inside the initializer closure, add the following code

var config = UIButton.Configuration.filled()

This created a basic configuration object based on the filled template.

Once you have a configuration object, there are many options you can modify to create the perfect button!

For example, you can change the size of the button and the style of its corners. To do that, add the following code underneath the code you just added:

config.buttonSize = .large
config.cornerStyle = .medium

This sets the button size to large and the corner style to medium. You could have a play with the different options here later if you wish.

Another option on UIButton.Configuration is titleTextAttributesTransformer, which allows you to modify properties of the button’s title label.

Additionally, there’s a similar property, subtitleTextAttributesTransformer, for modifying the button’s subtitle’s properties.

You’ll now set up a titleTextAttributesTransformer to change the font of the “Sign In” button.

Add the following underneath the code you just added:

config.titleTextAttributesTransformer =
  UIConfigurationTextAttributesTransformer { incoming in
    // 1
    var outgoing = incoming
    // 2
    outgoing.font = UIFont.preferredFont(forTextStyle: .headline)
    // 3
    return outgoing
  }

Here’s what you’re doing inside the transformer block:

  1. Create a mutable reference to the incoming property named outgoing so you can modify the attributes.
  2. Set the font of the title on outgoing.
  3. After that, return outgoing to apply the changes.
Note: outgoing and incoming are AttributeContainer types, which reflect several properties of UILabel.

Now the button’s title will appear with the correct font once the configuration is applied.

Adding Images

Another great feature of the Configuration API is the ability to add images to your buttons.

You want to add a chevron icon to the end of the “Sign In” button.

Add the following code underneath the code you just added:

// 1
config.image = UIImage(systemName: "chevron.right")
// 2
config.imagePadding = 5
// 3
config.imagePlacement = .trailing
// 4
config.preferredSymbolConfigurationForImage
  = UIImage.SymbolConfiguration(scale: .medium)

This code does the following:

  1. Initializes a UIImage with the chevron.right SF Symbol.
  2. Adds five points of padding between the title and the image.
  3. Places the image at the trailing end of the button.
  4. Sets the SF Symbol’s scale to .medium.
Note: If you are unfamiliar with SF Symbols then you might want to check out our SF Symbols tutorial.

It’s almost time to test this new configuration on signInButton!

The last thing to do is tell the button about the new config object. At the end of the initializer, right before the return statement, add the following code:

button.configuration = config

Now, build and run.

The sign in-page, with the Sign In button styled to have a large blue background which stretches to the edges of the screen

Excellent! The button is looking beautiful with its new style :]

However… if you tap on the button, the new style makes the interaction feel a bit clunky:

An animated gif of the sign-in page, where the Sign In button is tapped and changes text to Signing In, then back to Sign In after a one-second delay.

To be clear: When you tap on the button, it’s hard to see that the sign-in request is processing. Wouldn’t it be great if the button style updated automatically when the sign-in is in progress?

Introducing the Configuration Update Handler

As it turns out, there is a way to update the button automatically when the state changes.

Introducing… configurationUpdateHandler!

configurationUpdateHandler is a closure called when the button’s configuration changes. You can use it to change the configuration based on your app’s state.

SignInViewController has a Boolean property named signingIn, which you’ll use inside configurationUpdateHandler to change the button’s style.

Add the following code right before you set button.configuration:

button.configurationUpdateHandler = { [unowned self] button in
  // 1
  var config = button.configuration

  // 2
  config?.showsActivityIndicator = self.signingIn
  // 3
  config?.imagePlacement = self.signingIn ? .leading : .trailing
  // 4
  config?.title = self.signingIn ? "Signing In..." : "Sign In"

  // 5
  button.isEnabled = !self.signingIn

  // 6
  button.configuration = config
}

Here’s what you’re doing in this code:

  1. First, grab a copy of the button’s configuration and store it in a variable so you can modify it.
  2. Display the activity indicator if the user is signing in.
  3. The activity indicator’s position relative to the title is based on imagePlacement, so if the user is signing in, ensure it appears on the .leading edge, and if they aren’t signing in, revert back to .trailing to properly show the chevron.
  4. Set the title to Signing In… when the user is signing in, and set it to Sign In when they aren’t.
  5. Disable the button if the user is signing in.
  6. Finally, update the button’s configuration so your changes are reflected.

As you saw, UIButton.Configuration has a property named showsActivityIndicator. When true, the button’s image (regardless of whether it has one or not) will be replaced with an activity indicator.

The indicator’s position and padding relative to the title is based on imagePlacement and imagePadding.

The last thing to do is inform the button that the configuration needs to be updated whenever signingIn changes.

Find the signingIn property and replace the contents of the didSet block with the following code:

signInButton.setNeedsUpdateConfiguration()

setNeedsUpdateConfiguration informs the system that the button’s configuration needs to change.

Build and run.

An animated gif of the sign-in page, where the Sign In button is tapped and changes text to Signing In, and the background becomes translucent with a spinning activity indicator to the right of to the title before reverting back its original style after a one-second delay.

Looking great!

Next, you can apply a similar style to another button.

Styling the Get Help Button

Find the helpButton property. At the top of the initialization closure add the following code:

var config = UIButton.Configuration.tinted()
config.buttonSize = .large
config.cornerStyle = .medium

Then once again set the configuration right before the return statement like so:

button.configuration = config

Build and run.

The sign-in page, with styling applied to the Sign In and Get Help buttons

Excellent! Now, you’ll learn about how to create toggle buttons.

Creating Toggle Buttons

A wonderful use case of configurationUpdateHandler is that of toggle buttons.

Using the update handler, you can change a button’s icon based on its current state.

Build and run the app. The first screen you see if the BookListViewController.

The list of books, where the trailing add to cart button displays the text 'Add'

Notice on each of the book cells, there’s a button for adding them to the cart, located on the trailing edge.

If you tap on one, the text changes from “Add” to “Remove”. While this isn’t a bad user experience, implementing the new button configuration API will make it much better.

First, open BookCollectionCell.swift.

Next, locate the addToCartButton property. At the top of its initializer closure add the following code:

var config = UIButton.Configuration.gray()

This sets up a new configuration object from the gray template this time.

Then, set a default image for the button using the cart.badge.plus SF Symbol:

config.image = UIImage(systemName: "cart.badge.plus")

As you’ve done a few times now, set the button’s configuration right before the return statement:

button.configuration = config

Great job! Now, build and run.

The book list, with the trailing add to cart button that now has an icon of a cart next to the Add text, along with a gray background

The icon appears correctly, however, the title feels unnecessary, and the icon doesn’t change when a book is added to the cart. You’ll fix this now.

Replace this line of code, which sets the button’s title:

button.setTitle("Add", for: .normal)

With configurationUpdateHandler:

button.configurationUpdateHandler = { [unowned self] button in
  // 1
  var config = button.configuration

  // 2
  let symbolName = self.isBookInCart ? "cart.badge.minus" : "cart.badge.plus"
  config?.image = UIImage(systemName: symbolName)

  // 3
  button.configuration = config
}

Here’s what happens in the code above:

  1. As you did earlier, grab a mutable copy of the button’s current configuration.
  2. Modify config‘s image property based on the isBookInCart of the BookCollectionCell class.
  3. Finally, update the button’s configuration.

Once again we need to tell the button to update its configuration at the right time. In this case it is when isBookInCart changes.

Find the isBookInCart property. Then change the contents of the didSet handler with the following code:

addToCartButton.setNeedsUpdateConfiguration()

Build and run.

The book list, with the trailing add to cart button only displaying the icon

It works perfectly! The image changes to reflect whether the book is in the user’s cart.

Now it’s time to check out!

Creating Pop-Up Buttons

Tap the cart button in the navigation bar and gaze upon the unstyled buttons.

The cart screen, with a bottom panel that is unsettled. There are two buttons: Shipping Speed and Checkout

Additionally, if you change the shipping speed, there’s no indication of the current shipping selection.

Oh, the horror! Fortunately, you have the tools to fix this up in a jiffy!

To get started, you’ll style the shipping speed button and configure it so the current shipping speed appears on the label.

Open CartPanelView.swift.

First, locate the shippingSpeedButton property. Next, at the top of the initializer closure add the following code:

var config = UIButton.Configuration.tinted()
config.buttonSize = .medium
config.cornerStyle = .medium
config.titleTextAttributesTransformer = 
  UIConfigurationTextAttributesTransformer { incoming in
    var outgoing = incoming
    outgoing.font = UIFont.preferredFont(forTextStyle: .headline)
    return outgoing
  }

This creates a tinted configuration and sets its size and corner style. Then creates a title text transformer to set the button’s font.

Once again add the following code right before the return statement:

button.configuration = config

Build and run, and you’ll see the new styling applied to the shipping speed button.

The cart screen, with the Shipping Speed button now styled with a tinted background

However, selecting a shipping speed still doesn’t change the button’s title. You fix this next.

Changing the Button’s Title

Changing the title is an extremely easy job using the new changesSelectionAsPrimaryAction on UIButton!

This button uses the menu property to set a menu that pops up when the button is tapped. The new changesSelectionAsPrimaryAction property allows you to tell UIKit that it should use the selection from the menu as the title of the button.

Add the following code right after button.showsMenuAsPrimaryAction = true:

button.changesSelectionAsPrimaryAction = true

Now, build and run, and change the shipping speed in the cart view:

The cart screen, where the Shipping Speed button now displays the current speed as the button's title. The user taps on it and switches between Express Shipping and Standard Shipping to show the change in button title

Awesome! There’s now an easy way to view and change the shipping speed of the order.

All that’s left is to make the checkout button styled, and then the app will be complete!

Styling the Checkout Button

Locate the checkoutButton property, and at the top of the initializer closure, create a basic configuration for it like you’ve done several times now:

var config = UIButton.Configuration.filled()
config.buttonSize = .large
config.cornerStyle = .medium
config.imagePlacement = .leading
config.imagePadding = 5
config.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { incoming in
  var outgoing = incoming
  outgoing.font = UIFont.preferredFont(forTextStyle: .headline)
  return outgoing
}

You should be aware by now of what’s happening here!

When you’re done, the checkout button will display an activity indicator as the order is placed. The image properties you set above will control its placement and padding between the title.

And once again add the following code right before the return statement:

button.configuration = config

Almost done! Add a configurationUpdateHandler to update the button style when state changes:

button.configurationUpdateHandler = { [unowned self] button in
  // 1
  var config = button.configuration

  // 2
  config?.showsActivityIndicator = self.checkingOut
  // 3
  config?.title = self.checkingOut ? "Checking Out..." : "Checkout"

  // 4
  button.isEnabled = !self.checkingOut

  // 5
  button.configuration = config
}

Here’s what’s is happening:

  1. Store button‘s configuration in a mutable variable.
  2. Show the activity indicator when the user is checking out (based on the checkingOut property of the class).
  3. Change the title based on whether or not the user is checking out.
  4. Additionally, disable the button when checking out.
  5. Finally, save the changes to the configuration.

The last step is to make the usual call to setNeedsUpdateConfiguration when the state changes.

Find the checkingOut property. Replace the contents of the didSet block with the following code:

checkoutButton.setNeedsUpdateConfiguration()

Excellent! Build and run, and navigate to the cart.

The cart screen, with styles correctly and fully applied. The Checkout button has an activity indicator when tapped, which reverts after a second.

When you tap the checkout button, it displays the activity indicator for a second before reverting back to its previous state.

The app is complete, and you’re now a button enthusiast!

Where to Go From Here?

You can download the completed project by clicking the Download Materials button at the top or bottom of this tutorial.

Now that you’ve learned the basics of the new UIButton Configuration API, maybe you want to dive a little deeper and look at every property to see what else you can accomplish.

You can take a look at Apple’s documentation for Configuration to become a button expert.

Or, take a look at another project from Apple about the new configuration API.

We hope you enjoyed this tutorial, and if you have any questions or comments, please join the forum discussion below!

Contributors

Comments

Reviews

More like this