Home > front end >  How to stop a stroke repeat animation in SwiftUI
How to stop a stroke repeat animation in SwiftUI

Time:05-29

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

demo

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