Reputation: 30336
I made an extremely fun game where you toggle switches on and off. If all 3 are on, then the Rectangle
should flash green. Here's my code:
struct ContentView: View {
@State var toggle1IsOn = false
@State var toggle2IsOn = false
@State var toggle3IsOn = false
var body: some View {
VStack {
Toggle("Toggle 1", isOn: $toggle1IsOn)
Toggle("Toggle 2", isOn: $toggle2IsOn)
Toggle("Toggle 3", isOn: $toggle3IsOn)
Rectangle()
.fill(
toggle1IsOn && toggle2IsOn && toggle3IsOn
? Color.green /// if all 3 toggles turned on, then become green
: Color.gray
)
.animation(
toggle1IsOn && toggle2IsOn && toggle3IsOn
? .default.repeatForever(autoreverses: true)
: .default, /// remove forever animation if not all 3 toggles are on
value: toggle1IsOn
)
}
}
}
The color changes to green, but sometimes the repeat-forever animation doesn't run. Also, sometimes I get a weird purple color that I didn't define anywhere in my code. What is happening?
Upvotes: 3
Views: 1531
Reputation: 107
It's not good idea to use ternary operator for animation. Try to separate green rectangle and flash rectangle
struct ContentView: View {
@State var toggle1IsOn = false
@State var toggle2IsOn = false
@State var toggle3IsOn = false
@State private var flashRectangleDidAppear = false
var body: some View {
VStack {
Toggle("Toggle 1", isOn: $toggle1IsOn)
Toggle("Toggle 2", isOn: $toggle2IsOn)
Toggle("Toggle 3", isOn: $toggle3IsOn)
if toggle1IsOn && toggle2IsOn && toggle3IsOn {
flashRectangle
} else {
grayRectangle
}
}
}
var flashRectangle: some View {
Rectangle()
.fill(flashRectangleDidAppear ? Color.green : Color.gray)
.animation(.default.repeatForever(autoreverses: true), value: flashRectangleDidAppear)
.onAppear {
flashRectangleDidAppear.toggle()
}
}
var grayRectangle: some View {
Rectangle()
.fill(Color.gray)
}
}
Upvotes: 1
Reputation: 30336
The problem is in the value
of animation(_:value:)
. From the documentation:
A value to monitor for changes.
You're only passing in the first property toggle1IsOn
, so when the other ones are changed, the animation doesn't react. Instead, you should pass in the condition that causes the green to be shown:
value: toggle1IsOn && toggle2IsOn && toggle3IsOn
As long as value
conforms to Equatable
it's fine. You can also move it into a separate property, shouldFlash
, so you don't repeat code.
struct ContentView: View {
@State var toggle1IsOn = false
@State var toggle2IsOn = false
@State var toggle3IsOn = false
var body: some View {
/// accumulate into 1 constant
let shouldFlash = toggle1IsOn && toggle2IsOn && toggle3IsOn
VStack {
Toggle("Toggle 1", isOn: $toggle1IsOn)
Toggle("Toggle 2", isOn: $toggle2IsOn)
Toggle("Toggle 3", isOn: $toggle3IsOn)
Rectangle()
.fill(
shouldFlash
? Color.green
: Color.gray
)
.animation(
shouldFlash
? .default.repeatForever(autoreverses: true)
: .default,
value: shouldFlash /// here!
)
}
}
}
Result:
Upvotes: 2