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()
}
}
}
}