いろは2986023 views
中学数学621382 views
小学社会308636 views
Computer365120 views
LaTeX957300 views
りんご192546 views
教育148875 views
小学理科717236 views
高校国語785655 views
MathPython491378 views
Help
Tools

English

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 を実装し、標準で済む場合は標準を使うのが保守性の面でも有利だ。