0

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!

Leo
  • 1,508
  • 13
  • 27

1 Answers1

1

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 !!
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Makes sense, However, then also V2 is effected and animated when S1 changes which I tried to exclude. Is that even possible? – Leo May 07 '22 at 08:47
  • Wait, I can simply wrap the condition in a view and append the condition there. If you don't mind I'll edit the anser accordingly. Thx for kicking me into the right direction! – Leo May 07 '22 at 08:51
  • animation is activated by value, so if you put different animations by different values, then each of them will be activated independently; the case that they are applied to one container does not matter in general (but depending on view hierarchy there might be corner-cases). – Asperi May 07 '22 at 08:56
  • Yes, so in my specific case I had both views V1 and V2 depending on S1 but only V1 should be animated on chnages of S1 while V2 should change immediately when S1 changes. To get this as a result in the above excample, wrapping the if S1 { } condition in e.g. a HStack and then defining th eanimation on that HStack leads to the desired results. Don't get me wrong, I totally agree with your answer, it directly points out the key concept. I just want to make clear (also for any future help seekers), that to exactly meet all requirements of my question, the if condition has to be wrapped. – Leo May 07 '22 at 09:59