SwiftUI ButtonStyle の作成
ButtonStyle プロトコルを使うと、ボタンの見た目と動作を完全にカスタマイズできる。標準の .bordered や .borderedProminent では表現できないデザインを実現する際に活用する。
ButtonStyle の基本
ButtonStyle プロトコルは makeBody(configuration:) メソッドを要求する。
struct PrimaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 24)
.padding(.vertical, 12)
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}configuration.label には Button に渡されたコンテンツが入っている。これを装飾して返す。
Button("送信") {
submit()
}
.buttonStyle(PrimaryButtonStyle())押下状態の反映
configuration.isPressed でボタンが押されているかどうかを取得できる。
struct InteractiveButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 24)
.padding(.vertical, 12)
.background(configuration.isPressed ? Color.blue.opacity(0.7) : Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
.animation(.easeInOut(duration: 0.1), value: configuration.isPressed)
}
}押下時に色が変わり、少し縮小するインタラクションが追加された。
環境変数との連携
@Environment を使って、disabled 状態などを反映できる。
struct AdaptiveButtonStyle: ButtonStyle {
@Environment(\.isEnabled) var isEnabled
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 24)
.padding(.vertical, 12)
.background(isEnabled ? Color.blue : Color.gray)
.foregroundColor(isEnabled ? .white : .white.opacity(0.6))
.cornerRadius(8)
.opacity(configuration.isPressed ? 0.8 : 1.0)
}
}Button("送信") { }
.buttonStyle(AdaptiveButtonStyle())
.disabled(true) // グレーアウト表示カラーをパラメータ化
struct ColoredButtonStyle: ButtonStyle {
var color: Color
var textColor: Color = .white
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 24)
.padding(.vertical, 12)
.background(color)
.foregroundColor(textColor)
.cornerRadius(8)
.opacity(configuration.isPressed ? 0.8 : 1.0)
}
}
extension ButtonStyle where Self == ColoredButtonStyle {
static func colored(_ color: Color, text: Color = .white) -> ColoredButtonStyle {
ColoredButtonStyle(color: color, textColor: text)
}
}Button("確定") { }
.buttonStyle(.colored(.green))
Button("キャンセル") { }
.buttonStyle(.colored(.red))アウトラインスタイル
struct OutlineButtonStyle: ButtonStyle {
var color: Color = .blue
func makeBody(configuration: Configuration) -> some View {
configuration.label
.padding(.horizontal, 24)
.padding(.vertical, 12)
.foregroundColor(configuration.isPressed ? color.opacity(0.6) : color)
.background(
RoundedRectangle(cornerRadius: 8)
.stroke(color, lineWidth: 2)
)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(configuration.isPressed ? color.opacity(0.1) : Color.clear)
)
}
}枠線だけのボタンスタイル。押下時には薄い背景色がつく。
アイコン付きボタンスタイル
struct IconButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.labelStyle(.iconOnly)
.font(.title2)
.frame(width: 44, height: 44)
.background(
Circle()
.fill(configuration.isPressed ? Color.gray.opacity(0.2) : Color.clear)
)
}
}アイコンだけを表示するボタンに適したスタイル。
複数の ButtonStyle を定義しておく
PrimaryButtonStyle
メインアクション用。目立つ色で塗りつぶし。
SecondaryButtonStyle
サブアクション用。アウトラインまたは控えめな色。
DestructiveButtonStyle
削除など破壊的アクション用。赤系の色。
GhostButtonStyle
テキストリンク風。背景なし、押下時だけ反応。
struct GhostButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.foregroundColor(.blue)
.opacity(configuration.isPressed ? 0.5 : 1.0)
}
}標準スタイルとの併用
カスタム ButtonStyle を作らなくても、.buttonStyle(.bordered) や .buttonStyle(.borderedProminent) と .tint() の組み合わせで対応できる場合もある。
Button("標準") { }
.buttonStyle(.borderedProminent)
.tint(.green)完全なカスタマイズが必要な場合にのみ ButtonStyle を実装し、標準で済む場合は標準を使うのが保守性の面でも有利だ。











