Search code examples
swiftswiftuiswiftui-animationswiftui-transition

How to animate a view transition on a conditional view?


The setup is quite simple:

  • State/Observable/Computet property, let's call it S1
  • Views (V1, V2) that depend on S1

The following conditions must hold:

  • V1 should have a transition for in/out and be animated
  • Other views (e.g. V2) must not be animated when S1 changes
  • V2 is animatable by other states (e.g. S2)

Some consequences:

  • Using withAnimation { .. } on S1 would also lead to animating V2 unless animation is disabled here using .animation(nil).
  • However, V2.animation(nil) is not an option, because they still need to be animatable by the other state variable S2.
  • I therefore ruled out withAnimation { .. }

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 !!