swiftswiftuiswiftui-animationswiftui-transition

How to animate a view transition on a conditional view?


The setup is quite simple:

The following conditions must hold:

Some consequences:


I prepared the following example:

struct Test: View {
    @State private var S1: Bool = false
    @State private var S2: Bool = false
    
    func VX(_ text: String, color: Color = .red) -> some View {
        Text(text).padding(10).background(color).padding(10)
    }
    
    var transition: AnyTransition {
        .move(edge: .top).combined(with: .opacity)
    }
    
    var body: some View {
        VStack {
        
            if S1 {
                VX("V1")
                    .transition(transition)
                    .animation(.easeOut, value: S1) // <--- not working
            }
            
            VX("V2", color: S2 ? .red : .blue)
                .offset(x: S1 ? 30 : 0)
            
            Spacer()
            
            HStack {
                Button(action: changeS1) {
                    Text("Change S1")
                }
                
                Button(action: changeS2) {
                    Text("Change S2")
                }
            }
        }
    }
    
    func changeS1() {
        S1.toggle()
    }
    
    func changeS2() {
        S2.toggle()
    }
}

I think they way I implement the animation doesn't work because the view is conditionally inserted by the same variable and thus not present at the time where the value (S1) is changing thus .animation(.., value: S1) cannot detect any changes and thus no animation occurs.

So the question is: How to animate a view's transition based on a variable that determines it's conditional presence at the same time?

Any ideas are appreciated - thanks!


Solution

  • The .animation modifier should be applied to a container owning conditional view, so it could animate appear/disapper transition, like

        VStack {
        
            if S1 {
                VX("V1")
                    .transition(transition)
            }
           // ... other code
        }
        .animation(.easeOut, value: S1) // < here !!