SwiftUI Transition not happening

4.1k views Asked by At

I am new to SwiftUI and I am trying to use the .transition, but for some reason no transition happens.

You can see the code below:

View

import SwiftUI

struct ContentView: View {
  @ObservedObject var viewModel = ViewModel()
  
  var body: some View {
    if self.viewModel.model.show {
      Text("Showing")
        .padding()
    } else {
      Text("Not Showing")
        .padding()
        .transition(.asymmetric(insertion: .scale, removal: .opacity))
    }
    
    Button {
      self.viewModel.show()
    } label: {
      Text("Tap to change")
    }
  }
}

ViewModel

class ViewModel: ObservableObject {
  @Published private(set) var model = Model()
  
  func show() {
    self.model.toggleShow()
  }
}

Model

struct Model {
  var show: Bool = true
  
  mutating func toggleShow() {
    self.show.toggle()
  }
}

When I tap the button the text changes but no transition occurs.

I feel like I am missing something trivial here.

Can anyone please assist?

2

There are 2 answers

1
Asperi On BEST ANSWER

You need an animation (to animate transition) and a container (which performs actual transition, because default implicit Group does not do that).

Here is fixed part of code (tested with Xcode 13.2 / iOS 15.2)

*Note:Preview > Debug > Slow Animation for better visibility

demo

var body: some View {
    VStack {                            // << this !!
        if self.viewModel.model.show {
            Text("Showing")
                .padding()
        } else {
            Text("Not Showing")
                .padding()
                .transition(.asymmetric(insertion: .scale, removal: .opacity))
        }
    }
    .animation(.default, value: self.viewModel.model.show)  // << here !!

    Button {
        self.viewModel.show()
    } label: {
        Text("Tap to change")
    }
}
0
HunterLion On

Your code is fine (besides the fact that you need a VStack wrapping the text and the button), you only need to tell SwiftUI to use the transition by wrapping the command inside withAnimation().

Here's what you simply need to do in ContentView (look at the Button):

    @ObservedObject var viewModel = ViewModel()
    
    var body: some View {
        VStack {
            if self.viewModel.model.show {
                Text("Showing")
                    .padding()
            } else {
                Text("Not Showing")
                    .padding()
                    .transition(.asymmetric(insertion: .scale, removal: .opacity))
            }
            
            
            Button {
                withAnimation {       // This is what you need to trigger the transition
                    self.viewModel.show()
                }
            } label: {
                Text("Tap to change")
            }
        }
        .animation(.easeIn, value: self.viewModel.show)

    }