27

I’m trying to get my views to animate/transition using .transition() on views. I use similar code from here and put .transition() to both conditional views.

struct Base: View {
    @State private var isSignedIn = false

    var body: some View {
        Group {
            if(isSignedIn){
                Home().transition(.slide)
            }else{
                AuthSignin(isSignedIn: self.$isSignedIn).transition(.slide)
            }
        }
    }
}

struct AuthSignin: View {
    @Binding var isSignedIn: Bool

    var body: some View {
        VStack {
            Button(action: {
                self.isSignedIn = true
            }) {
                Text("Sign In")
                    .bold()
                    .frame(minWidth: CGFloat(0), maxWidth: .infinity)
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(Color.white)
                    .cornerRadius(CGFloat(10))
            }.padding()
        }
    }
}

However, whenever I click on the "Sign In" button (with or without .transition()), the app will freeze for a second and then the Home() view will suddenly appear without any animation/transition. I've also tried to wrap self.isSignedIn = true in withAnimation but it still won't work. Any ideas or is there a better way to do this?

leftysauce
  • 403
  • 1
  • 4
  • 7

3 Answers3

19

Place your .transition on the container of the views that will switch, not each conditional view. Here's a trivial example from some code I have done (which works).

In the main View that needs to transition conditionally:

import SwiftUI

struct AppWrapperView: View {

  @State var showFirstRun:Bool = true

  var body: some View {
    ZStack {
      if (showFirstRun) {
        FirstRunView(showFirstRun: $showFirstRun)
      } else {
        Text("Some other view")
      }
    }
    .transition(.slide)
  }
}

Then, somewhere in the view that triggers the change in condition:

import SwiftUI

struct FirstRunView: View {

  @Binding var showFirstRun:Bool

  var body: some View {

    Button(action: {
      withAnimation {
        self.showFirstRun = false
      }
    }) {
      Text("Done")
    }
  }
}
codewithfeeling
  • 6,236
  • 6
  • 41
  • 53
  • 3
    Doesn't work for me :( Im trying to mimic Google Maps app and there when you press on search field presents overlay view with fade (opacity) transition. So it's only working when transition applied to "second" view and only to present (dismissing without animation) but doesn't work at all when it's applied to ZStack. – Serj Rubens Apr 12 '20 at 15:40
  • The working solution was about to add zIndex to at least "Second" view which you want to present or probably even better to add it to all ZStack views. Seems to be a bug. Found it here: https://stackoverflow.com/questions/57730074/transition-animation-not-working-in-swiftui – Serj Rubens Apr 12 '20 at 16:09
17

I had to put my if..else statement inside ZStack container instead of Group. Seems that Group was the main reason for broken animation in my case. Also, I applied .transition in combination with .animation to container instead of views.

ZStack {
  if(isSignedIn){
    Home()
  } else {
    AuthSignin(isSignedIn: self.$isSignedIn)
  }
}
.transition(.slide)
.animation(.easeInOut)
Tim
  • 317
  • 3
  • 7
  • 4
    `ZStack` instead of `Group` did the trick - thanks. – Peter Jul 05 '20 at 18:00
  • 1
    Replacing `Group` with `ZStack` solved my issue as well! Thanks for the tip! ‍♂️ – Serzhas Sep 03 '20 at 07:38
  • 3
    You have to watch out for the .zIndex(). If more than 1 view is placed into a ZStack, it can happen that the animation will only play on appearing, but not on the disappearing. The reason for this is the position of the view in the ZStack. When a view is dismissed, it is placed behind all other views in the ZStack. To Avoid problems with animations of any kind, set the .zIndex of every view in the ZStack. Also avoid using 0 for the index. – cseh_17 Apr 07 '21 at 16:50
  • As of Jul 22, 2023, Group is working for me instead of ZStack – Plato Jul 23 '23 at 02:07
-2

Put WithAnimation before self.isSignedIn = true

Chris
  • 7,579
  • 3
  • 18
  • 38
  • Yea I’ve tried it like I mentioned where I wrap `self.isSignedIn = true` in `withAnimation` but still. Now the problem seems to be like there would be a freeze for 2 seconds whenever a new complex view that was not displayed (for example Home()) is loaded. Hence no animation. – leftysauce Nov 28 '19 at 09:29
  • 1
    then you should show us your complex home view. there must be the problem. – Chris Nov 28 '19 at 09:44