5
4

More than 3 years have passed since last update.

SwiftUIのボタンで少し凝ったハイライト表現をするには?

Posted at

ButtonStyleによるハイライト表現

SwiftUI においてボタンのタップ中のハイライト表現をしたい場合、ButtonStyle を作り、configuration.isPressed によってハイライト時の見た目に変更します。以下の例ですと、タップ中はボタンの背景色が変わるようになっています。

Aug-29-2021 07-52-00.gif

struct CustomButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .foregroundColor(Color.white)
            // タップ中かどうかで背景色を変更する
            .background(configuration.isPressed ? Color.red : Color.blue)
            .cornerRadius(4)
    }
}

Button(action: {}, label: { Text("Button") })
    // 作ったボタンスタイルを適用する
    .buttonStyle(CustomButtonStyle())

少し凝ったハイライト表現をするには?

しかし、タップ中かどうかによってボタンの見た目を大胆に変えたい場合、上記のような ButtonStyle による方法だと限界があります。そこで、次のような感じでタップ中かどうかによってボタンの label を簡単に変更できるボタンコンポーネントを作ってみました。

HighlightableButton(
    action: {
        // Nothing to do.
    }, label: { configuration in
        CharacterView(character: .wolverine, isPressed: configuration.isPressed)
    })

isPressed の値によって View の中身を自由自在に変更できるので、こんな凝ったハイライト表示も実現できてしまいます。MARVEL API を使って SwiftUI の勉強中だったので、こんなこともしてみたくなってしまいました。

Aug-29-2021 08-02-29.gif

今回作った HighlightableButton は次のように内部で ButtonStyle を使いつつ、label については外部から設定できるようにしています。NavigationLink 版もありますよ。

import SwiftUI

struct HighlightableButton<Label>: View where Label: View {
    private let action: () -> Void
    private let label: (ButtonStyleConfiguration) -> Label

    init(action: @escaping () -> Void, @ViewBuilder label: @escaping (ButtonStyleConfiguration) -> Label) {
        self.label = label
        self.action = action
    }

    var body: some View {
        Button(action: action, label: {})
            .buttonStyle(HighlightableButtonStyle { config in
                label(config)
            })
    }
}

struct HighlightableNavigationLink<Label, Destination>: View where Label: View, Destination: View {
    private let destination: () -> Destination
    private let label: (ButtonStyleConfiguration) -> Label

    init(destination: @escaping () -> Destination, @ViewBuilder label: @escaping (ButtonStyleConfiguration) -> Label) {
        self.destination = destination
        self.label = label
    }

    var body: some View {
        NavigationLink(destination: destination, label: {})
            .buttonStyle(HighlightableButtonStyle { config in
                label(config)
            })
    }
}

private struct HighlightableButtonStyle<Label>: ButtonStyle where Label: View {
    private var label: (ButtonStyleConfiguration) -> Label

    init(@ViewBuilder label: @escaping (ButtonStyleConfiguration) -> Label) {
        self.label = label
    }

    func makeBody(configuration: Configuration) -> some View {
        label(configuration)
    }
}
5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4