Nested transitions / animations in SwiftUI

866 views Asked by At

The goal is to create a view on top on another view, that has elements and changes color while moving away to the top of the screen. So we have one variable / state that triggers an animation where both views have sub transitions / animations. Problem is: there is either no movement or no colortransition at some point in the animation / transition because adding a transition somehow overwrites all the individual transitions of the elements in the stack.

EDIT: Due to simplicity I rearranged my code avoiding the described behaviour. But it seems like a really bad solution (repeating lots of code, if's ...) Has someone an idea how to write the following working example in a more compact/advanced way?

Working, but probably unnecessary complicated:

struct ContentView: View {
    @State var signUpLoginView_active: Bool = true
    var body: some View {
        ZStack{
            Color.orange
            if signUpLoginView_active {
                ZStack {
                    Color.white
                }
                .transition(.move(edge: .top))
                .zIndex(1)
            }
            if signUpLoginView_active {
                ZStack {
                    Color.red
                }
                .transition(AnyTransition.move(edge: .top).combined(with: AnyTransition.opacity))
                .zIndex(2)
            }
            Button("test"){
                withAnimation(.easeInOut(duration: 2)){
                    signUpLoginView_active.toggle()
                }
            }.zIndex(3)
        }.ignoresSafeArea()
    }
}

My Approach to simplify (not working correctly):

struct ContentView: View {
    @State var signUpLoginView_active: Bool = true
    var body: some View {
        ZStack{
            Color.orange
            if signUpLoginView_active {
                ZStack {
                    Color.white
                    Color.red.opacity(signUpLoginView_active ? 1 : 0)
                }
                .transition(AnyTransition.move(edge: .top))
            }
            Button("test"){
                withAnimation(.easeInOut){
                    signUpLoginView_active.toggle()
                }
            }
        }.ignoresSafeArea()
    }
}
1

There are 1 answers

4
Mahdi BM On

Although it might get more tricky than this simple answer, you can combine transitions together so they take effect seamlessly:

.transition(AnyTransition.move(edge: .top)
            .combined(with: AnyTransition.scale))

In an optimal case, you should not need those AnyTransitions:

.transition(.move(edge: .top)
            .combined(with: .scale))

but it sometimes results in an Xcode error, which might or might not give any clue of the real problem that Xcode has with your code.

Optionally you might need to define whether your transitions should take effect only on removal or insertion, or ask for different Transitions on each of removal or insertion:

.transition(AnyTransition.asymmetric(insertion: AnyTransition.move(edge: .top),
                                 removal: AnyTransition.move(edge: .bottom))
            .combined(with: AnyTransition.asymmetric(insertion: .scale,
                                                     removal: .identity)))
                                        // `.identity` means no transition