0

I would like to make an animation that is like this: a ZStack with a view and over it, a purple rectangle, that initially has scale 0 and opacity 0 and is at the center of screen.

When the animation is triggered, two animations happen:

  1. the rectangle's scale increases from 0 to 1 in 0.4 seconds.
  2. the rectangle's opacity increases from 0 to 1 in 0.3 seconds and from 1 to 0 in 0.1 seconds.

This is me trying to do this:

struct ContentView : View { @State private var scale: CGFloat = 0 @State private var scaleSeed: CGFloat = 0.1 @State private var counter: Int = 1 @State private var triggerFlashAnimation = false { didSet { scale = 0 counter = 1 } } var body: some View { ZStack { Text("Hello") if triggerFlashAnimation { Rectangle() .background(Color.purple) .scaleEffect(scale) .onAppear { scale += scaleSeed counter += 1 } } } 

I think this will animate the scale but this is very crude as I don't have any control over time.

I remember back in the day of CoreGraphics that you could define keyframes for time and values.

How do I do that with SwiftUI. I mean a precise animation defining keyframes and values for every parameter?

I googled around and found nothing.

1
  • I don't think SwiftUI supports keyframe animation. Commented Feb 19, 2021 at 20:01

1 Answer 1

1

SwiftUI doesn't have support for keyframes like we're used to in CoreAnimation (sources: https://stackoverflow.com/a/56908148/560942, https://swiftui-lab.com/swiftui-animations-part1/).

However, you can do a variation on chaining by using delay:

struct ContentView : View { @State private var scale: CGFloat = 0 @State private var opacity: Double = 0 @State private var triggerFlashAnimation = false func triggerAnimationActions() { withAnimation(.linear(duration: 0.4)) { scale = 1 } withAnimation(.linear(duration: 0.3)) { opacity = 1 } withAnimation(Animation.linear(duration: 0.1).delay(0.3)) { opacity = 0 } // reset DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) { triggerFlashAnimation = false scale = 0 opacity = 0 } } var body: some View { ZStack { Text("Hello") Button(action: { triggerFlashAnimation = true }) { Text("Button") }.onChange(of: triggerFlashAnimation) { (val) in if val { triggerAnimationActions() } } if triggerFlashAnimation { Rectangle() .fill(Color.purple.opacity(opacity)) .frame(width: 100, height: 100) .scaleEffect(scale) } }.frame(maxWidth: .infinity, maxHeight: .infinity) } } 

It's also probably worth looking into AnimatableModifier which lets you define an animatableData property where you can interpolate values, base exact frames on a certain state of the animation, etc, but, as far as I know, doesn't account for timing at all -- rather it just lets you set a state based on the precise value of the current animation. Good resource for reading about AnimatableModifier: https://www.hackingwithswift.com/quick-start/swiftui/how-to-animate-the-size-of-text

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.