How to Better Use ButtonStyle in SwiftUI
SwiftUI provides a powerful way to create and customise views using styling protocols. These protocols allow us to define the look and feel of our views in a single line of code.
As a result, the views are visually consistent, making the app easier to use and more attractive.
Styling Button without ButtonStyle
We could style our button inline by applying different modifiers to it. Consider this example:
Button("Tap me") {}
.font(.system(.callout, weight: .bold))
.foregroundColor(.white)
.frame(maxWidth: .infinity, maxHeight: 48)
.background(.blue)
.cornerRadius(8)
This will yield the following result:
This approach is completely fine if you design a single button. But if you need to develop multiple buttons with the same style you need to use the ButtonStyle
protocol. It will help share and apply the same style to different buttons across the app.
Styling Button with ButtonStyle
Let’s create our custom button style named PrimaryButtonStyle
. This is the style used for primary buttons throughout the application.
Let’s take a look at this example:
struct PrimaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(.system(.callout, weight: .bold))
.foregroundColor(.white)
.frame(maxWidth: .infinity, maxHeight: 48)
.background(.blue)
.cornerRadius(8)
}
}
To create a custom button style, we need to confirm to ButtonStyle
protocol and implement the makeBody(configuration:)
method. This method returns the body of the button, which is typically a view that can be positioned and styled to match the desired appearance.
To apply the created button style try writing this:
Button("Tap me") {}
.buttonStyle(PrimaryButtonStyle())
It is now possible to apply the PrimaryButtonStyle
to any button within your application, thus allowing you to create consistent and reusable buttons throughout your entire application.
ButtonStyleConfiguration
The button’s configuration object has several properties, including the isPressed
boolean value, which indicates whether or not the button is currently being pressed. We could use that to change our button style when a user taps a button. To do that, we could write something like this:
struct PrimaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(.system(.callout, weight: .bold))
.foregroundColor(.white)
.frame(maxWidth: .infinity, maxHeight: 48)
.background(configuration.isPressed ? .blue.opacity(0.75) : .blue)
.cornerRadius(8)
}
}
Now the button looks different in the normal or highlighted states:
Pro tip
Our code can be made cleaner and more readable by adding the following extension to the PrimaryButtonStyle
:
extension ButtonStyle where Self == PrimaryButtonStyle {
static var primary: PrimaryButtonStyle {
PrimaryButtonStyle()
}
}
Now we can apply our button style as easily as this:
Button("Tap me") {}
.buttonStyle(.primary)
Conclusion
With ButtonStyle
, we can define the appearance, layout, and interactions of a button and ensure consistency across the entire application.
In addition, it provides a way to encapsulate button-related logic and styling into reusable components. This makes the codebase more maintainable and scalable.
Thanks for reading!
If you find this article helpful, be sure to give it a clap so others can discover it more easily.