ButtonStyleによるハイライト表現
SwiftUI においてボタンのタップ中のハイライト表現をしたい場合、ButtonStyle
を作り、configuration.isPressed
によってハイライト時の見た目に変更します。以下の例ですと、タップ中はボタンの背景色が変わるようになっています。
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 の勉強中だったので、こんなこともしてみたくなってしまいました。
今回作った 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)
}
}