I am trying to choreograph animations in SwiftUI. I want two colored bars to move in to the view with a delay between them and then move out of the view with the delays switched so that the removal is the reverse of the insertion.
I think I understand why the below code doesn't work, but I can't figure out how to make it do what I need:
import SwiftUI
struct TestAnimControl: View {
@State var show: Bool = false
@State var reverseDelay: Bool = false
var body: some View {
VStack {
Button(action:{
self.show.toggle()
}) {
Text("Animate")
.font(.largeTitle)
}
if show {
Rectangle()
.foregroundColor(.blue)
.frame(height: 100)
.transition(.move(edge: .trailing))
.animation(Animation.spring().delay(show ? 0.3 : 0.5))
Rectangle()
.foregroundColor(.red)
.frame(height: 100)
.transition(.move(edge: .trailing))
.animation(Animation.spring().delay(show ? 0.5 : 0.3))
}
}
}
}
When you run this and hit the button, the blue bar moves in and then the red bar moves in. Hit the button again and the blue bar moves out and then the red bar moves out. What I want is when you hit the button for removal, the red bar moves out and then the blue bar moves out, reverse of the way the bars came in. In this code I believe the ternary doesn't work because the animation is set when the Rectangle is created and the delay can't change after that. I may be wrong, but either way is there a way to do what I am trying to do?
A proposed solution now is based on explicit animation with modification in every transaction so each transition have own parametrised variant of animation.

Main part:
Button(action:{
withAnimation {
self.show.toggle()
}
}) {
Text("Animate")
.font(.largeTitle)
}
if show {
Rectangle()
.foregroundColor(.blue)
.frame(height: 100)
.transition(.move(edge: .trailing))
.transaction {
$0.animation = Animation.spring().delay(delay1)
}
.onAppear { self.delay1 = 0.5 }
.onDisappear { self.delay1 = 0.3 }
Rectangle()
.foregroundColor(.yellow)
.frame(height: 100)
.transition(.move(edge: .trailing))
.transaction {
$0.animation = Animation.spring().delay(delay2)
}
.onAppear { self.delay2 = 0.3 }
.onDisappear { self.delay2 = 0.5 }
}
Test code on GitHub
!!! It does not work anymore with modern OS
Here is a solution - based on applied implicit animations to every transition. Tested with Xcode 11.4 / iOS 13.4
struct TestAnimControl: View {
@State var show: Bool = false
@State var reverseDelay: Bool = false
@State var blueDelay = 0.3
@State var redDelay = 0.5
var body: some View {
VStack {
Button(action:{
self.show.toggle()
}) {
Text("Animate")
.font(.largeTitle)
}
if show {
Rectangle()
.foregroundColor(.blue)
.frame(height: 100)
.transition(.move(edge: .trailing))
.animation(Animation.spring().delay(blueDelay))//(show ? 0.3 : 0.5))
.onAppear { self.blueDelay = 0.5 }
.onDisappear { self.blueDelay = 0.3 }
Rectangle()
.foregroundColor(.red)
.frame(height: 100)
.transition(.move(edge: .trailing))
.animation(Animation.spring().delay(redDelay))//(show ? 0.5 : 0.3))
.onAppear { self.redDelay = 0.3 }
.onDisappear { self.redDelay = 0.5 }
}
}
}
}
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