Button Styles in SwiftUI (iOS and macOS)

Button Styles in SwiftUI (iOS and macOS)

A good iOS app has buttons that stand out and feel intuitive. Applying a button style can make your UI more colorful, inviting and user-friendly. A well designed button grabs the user’s attention, while a simple button with no background, border, or color looks boring.

To apply a button style, use the .buttonStyle modifier on a Button in SwiftUI.

Button("Press me!", action: {})
	.buttonStyle(.bordered)

You can even create custom button styles, which we’ll discuss later. Let’s start by listing the built-in button styles in SwiftUI on iOS and macOS.

Recommended music to listen to while reading the article: Style (it's about SwiftUI, I promise).

All Button Styles

Here’s a list of SwiftUI button styles and how they look on iOS and macOS. (PrimitiveButtonStyle - Apple Developer Documentation)

iOS

Button("Automatic", action: {})
  .buttonStyle(.automatic)

Button("Bordered", action: {})
  .buttonStyle(.bordered)

Button("Bordered Prominent", action: {})
  .buttonStyle(.borderedProminent)

Button("Borderless", action: {})
  .buttonStyle(.borderless)

Button("Plain", action: {})
  .buttonStyle(.plain)
SwiftUI button styles on iOS showing automatic, bordered, bordered prominent, borderless, and plain buttons.
iOS: Button Styles in SwiftUI

macOS

Button("Automatic", action: {})
	.buttonStyle(.automatic)
	
Button("Accessory bar", action: {})
	.buttonStyle(.accessoryBar)

Button("Accessory bar action", action: {})
	.buttonStyle(.accessoryBarAction)

Button("Bordered", action: {})
	.buttonStyle(.bordered)

Button("Bordered Prominent", action: {})
	.buttonStyle(.borderedProminent)

Button("Borderless", action: {})
	.buttonStyle(.borderless)

Button("Link", action: {})
	.buttonStyle(.link)

Button("Plain", action: {})
	.buttonStyle(.plain)
SwiftUI button styles on macOS showing automatic, accessory bar, bordered, link, and plain buttons.
macOS: Button Styles in SwiftUI

Button Color: .tint

To change the button color, use the .tint modifier. The .tint modifier behaves differently based on the button style. Sometimes it changes the text color (.borderless), sometimes the background color (.borderedProminent), and other times it doesn’t change anything (.plain).

Confused, because "tint" is not consisent

Applying .tint(.green) to every type of button:

SwiftUI buttons on iOS with .tint(.green) modifier
iOS: Button with tint color
SwiftUI buttons on macOS with .tint(.green) modifier
macOS: Button with tint color

Text Color: .foregroundStyle

To change only the text color of a button, use the .foregroundStyle modifier.

Applying .foregroundStyle(.orange) to every type of button:

SwiftUI buttons on iOS using .foregroundStyle(.orange) modifier to change text color.
iOS: Button with colored foreground
SwiftUI buttons on macOS with .foregroundStyle(.orange) modifier to change text color.
macOS: Button with colored foreground

On macOS, applying .foregroundStyle to the button might not be enough to change the text color. The solution is to apply color directly to the label, not the button. (After .tint are we even surprised that it's not consistent?)

Button(action: {}, label: {
	Text("Bordered prominent")
		.foregroundStyle(.orange)
})
.buttonStyle(.borderedProminent)
SwiftUI buttons on macOS showing correct use of .foregroundStyle(.orange) applied directly to the text label.
macOS: Button with colored label

Custom Button Style

You can also create custom button styles in SwiftUI that fit your design needs. Because you’re “not like the other girls”. You have your own button style.

Taylor Swift recommending SwiftUI over UIKit (100% actually happened)
struct FancyButtonStyle: PrimitiveButtonStyle {
	func makeBody(configuration: Configuration) -> some View {
		configuration.label
			.padding()
			.background(
			  .linearGradient(colors: [.blue, .purple], startPoint: .topLeading, endPoint: .bottomTrailing)
			)
			.font(.title)
			.foregroundStyle(.white)
			.clipShape(.capsule)
	}
}
Button("Button with fancy style", action: {})
	.buttonStyle(FancyButtonStyle())
SwiftUI button using custom FancyButtonStyle with gradient background and capsule shape.
Button with a custom style

To make it easier to use, extend the PrimitiveButtonStyle class and add your custom style as a static variable.

extension PrimitiveButtonStyle where Self == FancyButtonStyle {
	static var fancy: FancyButtonStyle {
		FancyButtonStyle()
	}
}

This way you can apply your custom style like the default styles, using .fancy.

Button("Button with fancy style", action: {})
	.buttonStyle(.fancy)

Common Mistake: Style a Button’s Label

A common mistake is applying modifiers like .padding or .background directly to the button instead of its label.

Take this example:

Button(action: {
	print("Button tapped")
}, label: {
	Text("Try to tap me!")
})
.padding(120)
.background(.orange)
0:00
/0:04

SwiftUI button example showing incorrect padding applied outside the label, reducing tappable area.

Even though we added padding to the button, it doesn’t react when pressing the orange area, only when pressing the text.

The solution is to always add styling to the label, instead of the whole button.

Button(action: {
	print("Button tapped")
}, label: {
	Text("Try to tap me!")
		.padding(120)
		.background(.orange)
})
0:00
/0:06

SwiftUI button example showing correct padding applied to the label, making the full button area tappable.

This way you make sure that the entire area of the button is tappable.

Button Role and Style

Button roles such as .destructive and .cancel also affect appearance.

For example, a button with the .destructive role usually changes the text color to red on iOS, but not on macOS.

Button("Delete", role: .destructive, action: {})
SwiftUI button on iOS using .destructive role, showing red text appearance.
iOS: Destructive button style
SwiftUI button on macOS using .destructive role, showing unchanged default appearance.
macOS: Destructive button style

Even if you apply .tint of the button, the role takes precedence on iOS.

Button("Delete", role: .destructive, action: {})
	.tint(.green)  // has no effect because of the '.destructive' role

Button style also comes into play when using different roles. When the .destructive role is used for a button with .borderedProminent style, the background of the button also changes on iOS. As previously, on macOS the button is not affected and uses the default color.

Button("Delete", role: .destructive, action: {})
	.buttonStyle(.borderedProminent)
SwiftUI .borderedProminent button on iOS with .destructive role, showing red background effect.
iOS: Destructive button with style
SwiftUI .borderedProminent button on macOS with .destructive role, showing default background.
macOS: Destructive button with style

Not Only Buttons: .buttonStyle Works on Other Views Too

The .buttonStyle modifier isn’t limited to buttons. It can also be used on Toggle, Picker and Menu items.

In this article, we focus on the Button view since that’s where .buttonStyle is most often used.

Next episode: roles

You got a small taste of button roles, but the main course is yet to come. To learn why roles matter (not only in society, but also in SwiftUI) subscribe and tune in for the next article.