Reputation: 103
I want to be able to use a different transition depending on the state transition. For example, if I go from .one => .three, I want to fade out ViewOne on removal. If I go from .one => .two, I want to move ViewOne to the left on removal. Right now I have something like this (which clearly, only works for a transition from .one => .two):
ZStack {
if state == .one {
ViewOne()
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
if state == .two {
ViewTwo()
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
if state == .three {
ViewThree()
.transition(.asymmetric(insertion: .opacity, removal: .identity))
}
}
How can I dynamically change the transition based on the state?
UPDATE: Here's a more complete example:
enum Screen {
case susi, onboarding, home
}
struct ContentView: View {
@State var screen: Screen = .susi
@State var transition: AnyTransition = .asymmetric(insertion: .identity, removal: .move(edge: .leading))
var body: some View {
ZStack {
if screen == .susi {
ViewOne()
.transition(transition)
}
if screen == .onboarding {
ViewTwo()
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .leading)))
}
if screen == .home {
ViewThree()
.transition(.asymmetric(insertion: .opacity, removal: .identity))
}
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
withAnimation(.default) {
self.transition = .asymmetric(insertion: .identity, removal: .opacity)
self.screen = .home
}
}
}
}
}
Upvotes: 5
Views: 914
Reputation: 103
For those reading this with the same issue, one solution was toggling the id
property of a view. Some searching around SO lead me to this technique, which apparently resets the animations for a view.
import SwiftUI
enum Screen {
case susi, onboarding, home
}
struct ContentView: View {
@State var screen: Screen = .susi
@State var transition: AnyTransition = .move(edge: .leading)
@State var viewId = UUID().uuidString
var body: some View {
ZStack {
if screen == .susi {
ViewOne()
.transition(transition)
}
if screen == .onboarding {
ViewTwo()
.transition(.asymmetric(insertion: .move(edge: .trailing), removal: .move(edge: .trailing)))
}
if screen == .home {
ViewThree()
.transition(.asymmetric(insertion: .opacity, removal: .move(edge: .bottom)))
}
}
.id(viewId)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
withAnimation(.default) {
self.screen = .onboarding
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
withAnimation(.default) {
self.screen = .susi
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.transition = .opacity
self.viewId = UUID().uuidString
withAnimation(.default) {
self.screen = .home
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.transition = .move(edge: .top)
self.viewId = UUID().uuidString
withAnimation(.default) {
self.screen = .susi
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
self.transition = .move(edge: .leading)
self.viewId = UUID().uuidString
withAnimation(.default) {
self.screen = .onboarding
}
}
}
}
}
}
}
}
}
I ended up instead modifying the id
of ViewOne
instead of the entire ZStack, out of fear that doing so would yield some unexpected SwiftUI quirks down the road. Same technique, just applying it to a different view.
Upvotes: 2