I'm trying to create a really simple transition animation that shows/hides a message in the center of the screen by tapping on a button:
struct ContentView: View { @State private var showMessage = false var body: some View { ZStack { Color.yellow VStack { Spacer() Button(action: { withAnimation(.easeOut(duration: 3)) { self.showMessage.toggle() } }) { Text("SHOW MESSAGE") } } if showMessage { Text("HELLO WORLD!") .transition(.opacity) } } } }
According to the documentation of the .transition(.opacity)
animation
A transition from transparent to opaque on insertion, and from opaque to transparent on removal.
the message should fade in when the showMessage
state property becomes true and fade out when it becomes false. This is not true in my case. The message shows up with a fade animation, but it hides with no animation at all. Any ideas?
EDIT: See the result in the gif below taken from the simulator.
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.
If you want a SwiftUI view to start animating as soon as it appears, you should use the onAppear() modifier to attach an animation.
default is a basic animation with 0.35 seconds duration and an ease-in-ease-out timing curve.
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.
The problem is that when views come and go in a ZStack, their "zIndex" doesn't stay the same. What is happening is that the when "showMessage" goes from true to false, the VStack with the "Hello World" text is put at the bottom of the stack and the yellow color is immediately drawn over top of it. It is actually fading out but it's doing so behind the yellow color so you can't see it.
To fix it you need to explicitly specify the "zIndex" for each view in the stack so they always stay the same - like so:
struct ContentView: View { @State private var showMessage = false var body: some View { ZStack { Color.yellow.zIndex(0) VStack { Spacer() Button(action: { withAnimation(.easeOut(duration: 3)) { self.showMessage.toggle() } }) { Text("SHOW MESSAGE") } }.zIndex(1) if showMessage { Text("HELLO WORLD!") .transition(.opacity) .zIndex(2) } } }
}
My findings are that opacity transitions don't always work. (yet a slide in combination with an .animation will work..)
.transition(.opacity) //does not always work
If I write it as a custom animation it does work:
.transition(AnyTransition.opacity.animation(.easeInOut(duration: 0.2))) .zIndex(1)
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