Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI perform action after Spring() animation has completed

I'm trying to figure out how to detect that an animation has completed in SwiftUI, to be specific: a Spring() animation. My first thought was to use a GeometryReader to detect when the Circle in the example below reaches the point of origin (offset = .zero), however there is one caveat to this approach: the Spring() animation goes a little bit beyond the point where it should end and then bounces back. So the "end of the animation" would be triggered before the animation has finished.

I did some research and found another approach : SwiftUI withAnimation completion callback. However, in this solution the offset of the animated object is compared to the point of origin so it's the same problem as described above.

I could use a timer but that wouldn't be an elegant solution since the duration of the Spring() animation dynamically changes depending from where it started, so that's not the way.

In the example below, I would like that the circle gets green after the animation has finished.

Is there a way to solve this issue? Thanks for helping!

enter image description here

struct ContentView: View {
    
    @State var offset: CGSize = .zero
    @State var animationRunning = false
    
    var body: some View {
        
        VStack {
            Circle()
                .foregroundColor(self.animationRunning ? .red : .green)
                .frame(width: 200, height: 200)
                .offset(self.offset)
                .gesture(
                    DragGesture()
                        .onChanged{ gesture in
                            self.offset = gesture.translation
                    }
                    .onEnded{_ in
                        self.animationRunning = true
                        
                        withAnimation(.spring()){
                            self.offset = .zero
                        }
                })
            Spacer()
        }
    }
}

like image 382
leonboe1 Avatar asked Aug 13 '20 06:08

leonboe1


People also ask

How do I use .animation in SwiftUI?

SwiftUI has built-in support for animations with its animation() modifier. To use this modifier, place it after any other modifiers for your views, tell it what kind of animation you want, and also make sure you attach it to a particular value so the animation triggers only when that specific value changes.

What is withAnimation SwiftUI?

withAnimation() takes a parameter specifying the kind of animation you want, so you could create a three-second linear animation like this: withAnimation(.linear(duration: 3)) Explicit animations are often helpful because they cause every affected view to animate, not just those that have implicit animations attached.

Does SwiftUI use Core Animation?

SwiftUI uses Core Animation for rendering by default, and its performance is great for most animations. But if you find yourself creating a very complex animation that seems to suffer from lower framerates, you may want to utilize the power of Metal, which is Apple's framework used for working directly with the GPU.


1 Answers

Default animation duration (for those animations which do not have explicit duration parameter) is usually 0.25-0.35 (independently of where it is started & platform), so in your case it is completely safe (tested with Xcode 11.4 / iOS 13.4) to use the following approach:

withAnimation(.spring()){
    self.offset = .zero
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        self.animationRunning = false
    }
}

Note: you can tune that 0.5 delay, but the difference is not remarkable for human eye.

like image 86
Asperi Avatar answered Oct 21 '22 23:10

Asperi