I am trying to create a custom ButtonStyle
which responds to changes in state. I can’t get it to update when the state changes.
Here is a simplified version:
struct TestButton: ButtonStyle {
@State var selected: Bool = true
func makeBody(configuration: ButtonStyleConfiguration) -> some View {
configuration.label
.frame(width: 48)
.padding(EdgeInsets(top: 3, leading: 4, bottom: 3, trailing: 4))
.background(selected ? .green : .red)
.cornerRadius(14)
}
}
struct TestView: View {
@AppStorage("test") var test = 1
var body: some View {
HStack {
Button(test == 1 ? "Yes" : "No") { test = 1 }
.buttonStyle(TestButton(selected: test == 1))
Button(test == 2 ? "Yes" : "No") { test = 2 }
.buttonStyle(TestButton(selected: test == 2))
Button(test == 3 ? "Yes" : "No") { test = 3 }
.buttonStyle(TestButton(selected: test == 3))
}
// .id(UUID()) // trying to avoid this
.padding()
}
}
The variable test
is set by the buttons, and I want it to change the appearance of the button. The test test == …
works correctly as the text switches between Yes
and No
. However, when I use it with the selected
parameter, it doesn’t change anything. It sets the value when the view is first loaded, but doesn’t update.
The variable in the View uses @AppStorage
, but I’ve tried with @State
and get the same results.
I know that I can fake it by adding .id(UUID())
, but I’m not sure whether it’s the best approach.
Ho can I update the ButtonStyle
?
Just define selected
be a normal property. Here is an example, I just change @AppStorage
to a @State
to easier for testing:
struct TestButton: ButtonStyle {
let selected: Bool
func makeBody(configuration: ButtonStyleConfiguration) -> some View {
configuration.label
.frame(width: 48)
.padding(EdgeInsets(top: 3, leading: 4, bottom: 3, trailing: 4))
.background(selected ? .green : .red)
.cornerRadius(14)
}
}
struct TestView: View {
@State var test = 1
var body: some View {
HStack {
Button(test == 1 ? "Yes" : "No") { test = 1 }
.buttonStyle(TestButton(selected: test == 1))
Button(test == 2 ? "Yes" : "No") { test = 2 }
.buttonStyle(TestButton(selected: test == 2))
Button(test == 3 ? "Yes" : "No") { test = 3 }
.buttonStyle(TestButton(selected: test == 3))
}
.padding()
}
}
When using @State var selected
in ButtonStyle
, It just recevie the value when a identity created, adding .id(UUID())
work because it cause current ButtonStyle to be destroy and a new Identity
to be created therefore ButtonStyle
will received selected
change. I recommend you to watch "Demystify SwiftUI" to have more understand.