SwiftUI Button Styles

The engineering community here at WillowTree, especially within the iOS practice, has been super excited about SwiftUI. It’s incredible how much power and flexibility it brings to our app development workflows. This blog post was created in conjunction with Will Ellis, who gave a wonderful talk showcasing the power of SwiftUI Button Styles. You can find a copy of the code here.

Here at WillowTree, developers often work hand in hand with the design team before implementing anything in an app. For example, when we create a component (buttons, icons, sliders, etc.), designers provide a design system that illustrates how they should look and be reused across an app. In addition, these designs can show how the components change based on their state. In this blog post, we’ll be looking to implement buttons based on the designs below. Luckily, SwiftUI has great features that allow us to support different states for our buttons and make it easy to bring our designs to reality.

Image of an array of button designs.

Option 1: Applying View Modifiers to Style a Button

SwiftUI already has a built-in Button component. Slap some view modifiers on that for styling, and boom, a button! 

Screenshot of an iPhone with a regular enabled button in the center.

With just a few view modifiers, it is easy to get started. However, as it is currently written, the styling is not encapsulated or reusable.

// Applying view modifiers to style a button
// Pros:
// ✅ Easy to get started
//
// Cons:
// ❌ Styling is not encapsulated or reusable, not DRY

Option 2: Wrapping Button with our own View

Another approach we can try is to create a custom button. To do this, we can give the custom button all the necessary view modifiers, and use initializers to pass in the action and label parameters. 

Screenshot of an iPhone with a regular “Custom” enabled button in the center. 

 

This approach gets the job done, but it doesn’t allow us to access the button’s state and requires too much boilerplate code. We can do better than this. 

// Wrapping Button with our own view 
// Pros: 
//    ✅ Encapsulated, reusable, and DRY styling 
// 
// Cons: 
//    ❌ Tight coupling of styling and content 
//    ❌ No access to button pressed state 
//    ❌ Have to duplicate Button boilerplate (e.g., 
//       initializers, generic Label type)

Option 3: Using Built-in ButtonStyles 

What if we wanted to use the built-in Button but also wanted to use Apple’s styling? This is where the .buttonStyle view modifier is useful. It has a variety of styling options from Apple that are pretty neat. It also gives us the option to set a role like .destructive that changes how the button functions. 

Screenshot of iPhone showing six buttons with varying styles

The main downside is that it uses Apple’s styling designs, rather than our own designs, which takes away from the customization of an element.  

// Apple provides us with built-in ButtonStyles
// Pros:
//   ✅ Work cross-platform
//   ✅ Easy to use right out of the box
//
// Cons:
//   ❌ Not what’s in our design

Option 4: Creating a Custom ButtonStyle

Apple has also given us the option of creating our own button style type. ButtonStyle is just a protocol, and all we need to do to conform our own type to it is to implement the protocol’s makeBody function. This is where we would also adjust all the view modifiers for the styling we want. The really neat thing about this option is that we now have access to the .isPressed functionality which allows us to modify how the button reacts to being pressed. For example, we can change the background color while the button is pressed. 

Screenshot of iPhone with two enabled buttons, one with a checkmark icon.

        

// Pros:
//   ✅ Encapsulated, reusable, and DRY styling
//   ✅ Access to button pressed state
//   ✅ Styling and content are decoupled
//   ✅ Can use existing button initializers
//
// Cons:
//   ❌ Easy. Almost too easy 🤨

We can then go further with a customized ButtonStyle by setting a color scheme based on our designs, using the environment value .isEnabled, creating a State variable for isHovered, etc. All these options allow us to manipulate how the button looks and feels within the app while maintaining the design we want, in an encapsulated, reusable way.  

This option also makes it easy to set the desired color scheme just by extending ButtonStyle and initializing the different color scheme combinations. We can even extend our ButtonStyle to add leading and trailing icons using generics. Be sure to check out CapsuleButtonStyle in the repo to see how these variations are implemented. 

Conclusion

Who knew buttons could be so fun? SwiftUI provides multiple options for customizing and styling buttons to match any design system. By exploring the different approaches, we gain a deeper understanding of the pros and cons of each option, so that we can make informed decisions about which approach is best for our particular use case. The iOS community here at WillowTree has enjoyed exploring all the cool things SwiftUI has to offer. Thanks for reading!