swiftswiftuiswiftui-animation

My Animation in SwiftUI is not working properly


I have a problem with animation in SwiftUI, if I'm using .animation, my animation is not working.

ZStack(alignment: .bottom) {
    VStack {
        Button("Button") {
            showView.toggle()
        }
        Spacer()
    }
    
    if showView {
        RoundedRectangle(cornerRadius: 30)
            .frame(height: UIScreen.main.bounds.height * 0.5)
            .transition(.move(edge: .bottom))
            .animation(.easeInOut, value: showView)
    }
}
.ignoresSafeArea(edges: .bottom)

But if I'm using withAnimation in my button's action closure, and delete .animation or keep it, it is working.

ZStack(alignment: .bottom) {
    VStack {
        Button("Button") {
            withAnimation {
                showView.toggle()
            }
        }
        Spacer()
    }
    
    if showView {
        RoundedRectangle(cornerRadius: 30)
            .frame(height: UIScreen.main.bounds.height * 0.5)
            .transition(.move(edge: .bottom))
            .animation(.easeInOut, value: showView)
    }
}
.ignoresSafeArea(edges: .bottom)

Can someone explain me how this works ? I'm trying with DispatchQueue.main.async, it's still the same.


Solution

  • You're defining 2 different states:

    1. With RoundedRectangle if showView = true
    2. Without RoundedRectangle if showView = false

    So, every time showView changes, the body will be re-rendered. If it toggles from false -> true, then RoundedRectangle will be displayed instantly without any animations because it's not here until showView = true. And if it toggles from true -> false, then it can be dismissed with animation because now, it's a part of the View. You may try this to make it show/disappear more smoothly:

    ZStack(alignment: .bottom) {
        VStack {
            Button("Button") {
                showView.toggle()
            }
    
            Spacer()
        }
    
        RoundedRectangle(cornerRadius: 30)
            .frame(height: showView ? (UIScreen.main.bounds.height * 0.5) : 0)
            .transition(.move(edge: .bottom))
            .animation(.easeInOut, value: showView)
    }
    .ignoresSafeArea(edges: .bottom)