How to control the Rectangle animation repeat or not by assigning binding property isRepeatAnimation?
What I expect is that after assigning true to isRepeatAnimation, the border width animate from 5.0 to 0.0 and back and forth, and the repeat animation is off after assigning false to isRepeatAnimation.
import SwiftUI
struct ContentView: View {
@Binding var isRepeatAnimation: Bool
@State var lineWidth: CGFloat = 5
var body: some View {
Rectangle()
.stroke(Color.blue, style: StrokeStyle(lineWidth: lineWidth))
.frame(width: 100, height: 100)
.animation(isRepeatAnimation ? repeatAnimation : Animation.easeInOut, value: lineWidth)
}
var repeatAnimation: Animation {
Animation.easeInOut.repeatForever(autoreverses: true)
}
}
struct ContentViewPreviewer: View {
@State var repeated = false
var body: some View {
VStack {
ContentView(isRepeatAnimation: $repeated)
Button("Toggle") {
repeated.toggle()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentViewPreviewer()
}
}
CodePudding user response:
To stop repeating animation it we should replace it with default one, and line width can be just toggled.
Tested with Xcode 13.4 / iOS 15.4
Here is main fixes:
Rectangle()
.stroke(Color.blue, style: StrokeStyle(lineWidth: isRepeatAnimation ? 0 : lineWidth))
.frame(width: 100, height: 100)
.animation(isRepeatAnimation ? repeatAnimation : Animation.default, value: isRepeatAnimation)
Complete test module in project
CodePudding user response:
If the purpose is to have the line width going from 5 to 0 to 5 and stop, this can be a solution. If the purpose is to manually stop animation, read Asperi's solution.
You can animate twice, first by changing the line width from 5 to 0 on the first half of the duration, then from 0 to 5 on the second half. You control this behaviour using two commands:
- on the
.animation(), include a pre-defined(duration:) - on change of the Boolean variable, change back from 0 to 5 using
DispatchQueue.main.asyncAfter()
Here's the sample code:
struct ContentView: View {
@Binding var animate: Bool
@State private var lineWidth = 5.0
var body: some View {
Rectangle()
.stroke(Color.blue, style: StrokeStyle(lineWidth: lineWidth))
// Instead of repeating, make it with a pre-defined duration.
// The value here (0.25) is half of the total duration
.animation(.easeInOut(duration: 0.25), value: lineWidth)
.frame(width: 100, height: 100)
// Change the line width when animate changes
.onChange(of: animate) { _ in
// Make it zero immediately
lineWidth = 0
// On the second half of the duration, make it 5 again
DispatchQueue.main.asyncAfter(deadline: .now() 0.25) {
lineWidth = 5
}
}
}
}
struct ContentViewPreviewer: View {
@State var animate = false
var body: some View {
VStack {
ContentView(animate: $animate)
Button("Toggle") {
animate.toggle()
}
}
}
}

