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.
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:
- Create a mutable reference to the
incoming
property namedoutgoing
so you can modify the attributes. - Set the font of the title on
outgoing
. - After that, return
outgoing
to apply the changes.
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:
- Initializes a
UIImage
with thechevron.right
SF Symbol. - Adds five points of padding between the title and the image.
- Places the image at the trailing end of the button.
- Sets the SF Symbol’s scale to
.medium
.
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.
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:
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:
- First, grab a copy of the button’s
configuration
and store it in a variable so you can modify it. - Display the activity indicator if the user is signing in.
- 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. - Set the title to Signing In… when the user is signing in, and set it to Sign In when they aren’t.
- Disable the button if the user is signing in.
- 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.
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.
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
.
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 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:
- As you did earlier, grab a mutable copy of the button’s current configuration.
- Modify
config
‘simage
property based on theisBookInCart
of theBookCollectionCell
class. - 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.
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.
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.
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:
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:
- Store
button
‘sconfiguration
in a mutable variable. - Show the activity indicator when the user is checking out (based on the
checkingOut
property of the class). - Change the title based on whether or not the user is checking out.
- Additionally, disable the button when checking out.
- 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.
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!
Comments