Neil Macy

SwiftUI ButtonStyle

A while ago, I created a button in SwiftUI. It's a simple button, but I want to be able to use this design in lots of places in my app. It has the app's theme colours, it has a particular font, it has a pill-shaped rounded appearance.

I initially created this as a View, wrapping a Button with some customisations.

struct PurpleButton: View {
    let buttonTitle: String
    let action: () -> Void

    var body: some View {
        Button(buttonTitle, action: action)
            .frame(maxWidth: .infinity)
            .padding(EdgeInsets(top: 16, leading: 32, bottom: 16, trailing: 32))
            .font(.system(.title3, design: .rounded))
            .foregroundStyle(Color(.darkBackgroundText))
            .background(Color(.primary))
            .clipShape(Capsule())
    }
}

This gives me a PurpleButtonView, whose sole contents are a Button, styled how I want. But SwiftUI has a better way.

By using a ButtonStyle, you can customise a Button's appearance without wrapping it, avoiding creating that extra view in the view hierarchy.

struct PurpleButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .frame(maxWidth: .infinity)
            .padding(EdgeInsets(top: 16, leading: 32, bottom: 16, trailing: 32))
            .font(.system(.title3, design: .rounded))
            .foregroundStyle(Color(.darkBackgroundText))
            .background(Color(.primary))
            .clipShape(Capsule())
    }
}

You can then create a Button in the normal, simple way, and apply the style.

#Preview {
    Button("Press Me") { print("Button pressed") }
        .buttonStyle(PurpleButtonStyle())
}

You can clean this up a bit by creating an extension on ButtonStyle to define a static variable.

extension ButtonStyle where Self == PurpleButtonStyle {
    static var purpleButton: Self {
        PurpleButtonStyle()
    }
}

#Preview {
    Button("Press Me") { print("Button pressed") }
        .buttonStyle(.purpleButton)
}

The extension works really well if you have a standard style but want to provide different customisations.

extension ButtonStyle where Self == PurpleButtonStyle {
    static var purpleButton: Self {
        PurpleButtonStyle(.standard)
    }

    static var secondaryPurpleButton: Self {
        PurpleButtonStyle(.secondary)
    }

    static var cancelPurpleButton: Self {
        PurpleButtonStyle(.cancel)
    }
}

This pattern appears throughout SwiftUI. Styles are a great way to customise a component's appearance, often quite heavily, without having to wrap or try to subclass a core component.

Resources

That's quite a simple example for one button style. Here are some resources I found useful when learning about ButtonStyle:

Published on 6 September 2024