swiftanimationswiftui

SwiftUI withAnimation(_:_:) function doesn't animate


I couldn't manage to migrate a simple animation with the withAnimation(_:_:) function from this:

struct WelcomeView: View {

  let greetingTexts: [String]

  @ViewBuilder
  var body: some View {
    VStack {
      ForEach(greetingTexts.indices, id: \.self) { index in
        Text(greetingTexts[index])
            .font(.mtgSans(.title1))
            .multilineTextAlignment(.center)
            .transition(.opacity)
            .animation(Animation.easeOut.speed(0.5).delay(0.08 * Double(index)))
      }
    }
  }
}

Animation.

To this:

  struct WelcomeView: View {

    let greetingTexts: [String]

    @ViewBuilder
    var body: some View {
      VStack {
        ForEach(greetingTexts.indices, id: \.self) { index in
          withAnimation(.easeOut.speed(0.5).delay(0.08 * Double(index))) {
            Text(greetingTexts[index])
              .font(.mtgSans(.title1))
              .multilineTextAlignment(.center)
              .transition(.opacity)
          }
        }
      }
    }
  }

And it doesn't animate at all. Any clue?


Solution

  • Animations need to be associated with a value that changes after the view is first shown, such as a change to a state variable. The examples you posted do not change after initial show, so there is no animation.

    The animation you linked to shows two lines of text that simply move up by a small amount. Both lines appear to move together. However, your examples are using a delay that is dependent on the index position, so I guess you are after a staggered animation.

    A simple boolean value can be used for triggering the animation. Some notes and suggestions:

    In the updated example below there is also a button, for repeating the animation.

    struct WelcomeView: View {
        let greetingTexts: [String] =
            ["The quick brown fox", "jumps over the lazy dog"]
        @State private var isShowing = false
    
        var body: some View {
            VStack {
                ForEach(Array(greetingTexts.enumerated()), id: \.offset) { index, text in
                    Text(text)
                        .font(.mtgSans(.title1))
                        .multilineTextAlignment(.center)
                        .opacity(isShowing ? 1 : 0)
                        .offset(y: isShowing ? 0 : 30)
                        .animation(
                            .easeOut.delay(isShowing ? 0.5 * Double(index + 1) : 0),
                            value: isShowing
                        )
                }
    
                Button("Toggle") { isShowing.toggle() }
                    .buttonStyle(.bordered)
                    .padding(.top, 100)
            }
            .onAppear { isShowing = true }
        }
    }
    

    Animation