Home > Software design >  SwiftUI withAnimation doesn't start until view appears
SwiftUI withAnimation doesn't start until view appears

Time:08-09

I have two views in a TabView, which are Page1 and Page2. The two pages have the same view in common: AnimatingText. Page2 has an additional button that starts the animation of AnimatingText.

When I tap the button in Page2, I expected the animation would start simultaneously in both Page1 and Page2. However, I found that the animation in Page1 is delaying until I switch back to Page1.

I assume this behavior is by design. But I want to implement any of the following behaviors (both are fine for me):

  1. When I tap the button in Page2, the animation of AnimatingText starts at the same time in Page1 and Page2.
  2. When I tap the button in Page2, the animation of AnimatingText starts in Page2. When I switch back to Page1, AnimatingText is rerendered with the new state without animation.

Is there any workaround that I can try? Thanks.

Code


import SwiftUI

struct AnimationTestView: View {
    @State var startAnimate: Bool = false
    
    var body: some View {
        TabView {
            Page1(startAnimate: $startAnimate)
                .tabItem {
                    Text("page1")
                }
            Page2(startAnimate: $startAnimate)
                .tabItem {
                    Text("page2")
                }
        }
    }
}

struct AnimatingText: View {
    @Binding var startAnimate: Bool
    var body: some View {
        VStack{
            Text("text1")
            if startAnimate {
                Text("text2")
            }
            Text("text3")
        }
    }
}

struct Page1: View {
    @Binding var startAnimate: Bool
    var body: some View {
        AnimatingText(startAnimate: $startAnimate)
    }
}

struct Page2: View {
    @Binding var startAnimate: Bool
    var body: some View {
        ZStack {
            AnimatingText(startAnimate: $startAnimate)
            VStack {
                Spacer()
            Button {
                withAnimation(.easeInOut(duration: 1)) {
                    startAnimate.toggle()
                }
            } label: {
                Text("tap here to animate view in page1")
            }
            }
        }

    }
}

struct AnimationTestView_Previews: PreviewProvider {
    static var previews: some View {
        AnimationTestView()
    }
}

CodePudding user response:

  1. When I tap the button in Page2, the animation of AnimatingText starts at the same time in Page1 and Page2.

I'd say it is impossible with built-in animation engine - there is nothing actually to animate till Page1 goes to screen.

  1. When I tap the button in Page2, the animation of AnimatingText starts in Page2. When I switch back to Page1, AnimatingText is rerendered with the new state without animation.

It is much simpler goal, just make animation explicitly local, like

var body: some View {
    ZStack {
        AnimatingText(startAnimate: $startAnimate)
        VStack {
          Spacer()
          Button {
            startAnimate.toggle()   // << just toggle state
          } label: {
            Text("tap here to animate view in page1")
          }
        }
    }
    .animation(.easeInOut(duration: 1), value: startAnimation) // local animation !!
}
  • Related