I'm trying to make a view that conditionally animates (repeating forever) based on the state of another view.
This below button does it all in one view... but that state is all internal. I want it to be external.
struct ButtonBouncerView: View {
@State var active: Bool = false
var body: some View {
Circle()
.scaleEffect(active ? 1.08: 1)
.animation(Animation.default.repeatForever(autoreverses: true), value: active)
.frame(width: 100, height: 100)
.onTapGesture {
useTransaction()
}
.onAppear {
active = true
}
}
func useTransaction() {
var transaction = Transaction()
transaction.disablesAnimations = active ? true : false
withTransaction(transaction) {
withAnimation {
active.toggle()
}
}
}
}
I tried to do it like this, using the Transactions API. However, this just has the effect of doing weird animations when I tap, basically doing some sort of weird wobbling like motion.
struct OutsideControllableButtonView: View {
@State private var active: Bool = false
var body: some View {
VStack {
OutsideControllableButtonScaler(active: $active)
Button(action: { // CONTROLLER BUTTON!
active.toggle()
}) {
Text("Control Button")
}
.padding()
.background(Color.green)
.foregroundColor(.white)
}
}
}
struct OutsideControllableButtonScaler: View {
// When Active, the button
@Binding var active: Bool
var body: some View {
Circle()
.scaleEffect(active ? 1.08: 1)
.animation(Animation.default.repeatForever(autoreverses: true), value: active)
.frame(width: 100, height: 100)
.onChange(of: active) { newValue in
var transaction = Transaction(animation: newValue ? Animation.default.repeatForever(autoreverses: true) : nil)
transaction.disablesAnimations = true
withTransaction(transaction) {
self.active = newValue
}
}
}
}
Any ideas on how to fix it?
You could do something like below, where you're setting what animation you want to use based on active
. I don't believe you can stop the repeatForever animation by setting it to nil, you actually need to update it to default...
Alternatively, you could write an extension on Animation
to make this a little cleaner/reusable. But at a quick test, the below starts and stops the animation as I believe you are attempting to do.
struct OutsideControllableButtonView: View {
@State private var active: Bool = false
var body: some View {
VStack {
OutsideControllableButtonScaler(active: $active)
Button(action: { // CONTROLLER BUTTON!
self.active.toggle()
}) {
Text("Control Button")
}
.padding()
.background(Color.green)
.foregroundColor(.white)
}
}
}
struct OutsideControllableButtonScaler: View {
// When Active, the button
@Binding var active: Bool
var body: some View {
Circle()
.scaleEffect(active ? 1.08 : 1)
.animation(active ? Animation.default.repeatForever(autoreverses: true) : .default, value: active)
.frame(width: 100, height: 100)
}
}