I'm using SwiftUI and I want to animate a view as soon as it appears (the explicit type of animation does not matter) for demo purposes in my app. Let's say I just want to scale up my view and then scale it down to its original size again, I need to be able to animate the view to a new state and back to the original state right afterward. Here's the sample code of what I've tried so far:
import SwiftUI
import Combine
struct ContentView: View {
@State private var shouldAnimate = false
private var scalingFactor: CGFloat = 2
var body: some View {
Text("hello world")
.scaleEffect(self.shouldAnimate ? self.scalingFactor : 1)
.onAppear {
let animation = Animation.spring().repeatCount(1, autoreverses: true)
withAnimation(animation) {
self.shouldAnimate.toggle()
}
}
}
Obviously this does not quite fulfill my requirements, because let animation = Animation.spring().repeatCount(1, autoreverses: true)
only makes sure the animation (to the new state) is being repeated by using a smooth autoreverse = true
setting, which still leads to a final state with the view being scaled to scalingFactor
.
So neither can I find any property on the animation
which lets my reverse my animation back to the original state automatically (without me having to interact with the view after the first animation), nor did I find anything on how to determine when the first animation has actually finished, in order to be able to trigger a new animation.
I find it pretty common practice to animate some View upon its appearance, e.g. just to showcase that this view can be interacted with, but ultimately not alter the state of the view. For example animate a bounce effect on a button, which in the end sets the button back to its original state. Of course I found several solutions suggesting to interact with the button to trigger a reverse animation back to its original state, but that's not what I'm looking for.
Here is a solution based on ReversingScale
animatable modifier, from this my answer
Complete test module is here
Tested with Xcode 11.4 / iOS 13.4
struct DemoReverseAnimation: View {
@State var scalingFactor: CGFloat = 1
var body: some View {
Text("hello world")
.modifier(ReversingScale(to: scalingFactor, onEnded: {
DispatchQueue.main.async {
self.scalingFactor = 1
}
}))
.animation(.default)
.onAppear {
self.scalingFactor = 2
}
}
}
Another approach which works if you define how long the animation should take:
struct ContentView: View {
@State private var shouldAnimate = false
private var scalingFactor: CGFloat = 2
var body: some View {
Text("hello world")
.scaleEffect(self.shouldAnimate ? self.scalingFactor : 1)
.onAppear {
let animation = Animation.easeInOut(duration: 2).repeatCount(1, autoreverses: true)
withAnimation(animation) {
self.shouldAnimate.toggle()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation(animation) {
self.shouldAnimate.toggle()
}
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With