cluelessCoder
cluelessCoder

Reputation: 1088

Animate SwiftUI View when .sheet is shown

I have a SheetAnimationView from which I want to show a sheet called SheetContentView.

When the sheet appears, I want to show a transition animation of its content (start the animation when the content appears) but am unable to make it work. All 3 views in VStack should ideally end their animation at the same time.

SheetContentView:

struct SheetContentView: View {
    @Binding var showSheet: Bool
    
    var body: some View {
        VStack(spacing: 8) {
            Text("A great content of my new sheet")
            Label("still not done", systemImage: "guitars")
            Text("I'm done now")
        }
        .transition(.asymmetric(insertion: .scale, removal: .opacity)) // <--- I want this to work
        .animation(Animation.easeInOut(duration: 2)) // <--- for 2 seconds
        
    }
}

SheetAnimationView:

struct SheetAnimationView: View {
    @State var showSheet: Bool = false
    
    var body: some View {
        Button("show my sheet with animated content (hopefully)") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            SheetContentView(showSheet: $showSheet)
        }
        
    }
}

Upvotes: 2

Views: 5361

Answers (2)

PetrV
PetrV

Reputation: 1368

Adding alternative way of using onAppear so you don't need to nest all views within if block. (Same result as gif in other answer)

struct SheetContentView: View {
    @State var scale = 0.0
    @State var opacity = 0.0
    
    var body: some View {
        VStack(spacing: 8) {
            Text("A great content of my new sheet")
            Label("still not done", systemImage: "guitars")
            Text("I'm done now")
        }
        .scaleEffect(scale)
        .opacity(opacity)
        .onAppear {
            withAnimation(.easeInOut(duration: 1)) {
                scale = 1.0
                opacity = 1.0
            }
        }
    }
}

struct SheetAnimationView: View {
    @State var showSheet: Bool = false
    
    var body: some View {
        Button("show my sheet with animated content (hopefully)") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            SheetContentView()
        }
        
    }
}

Upvotes: 0

Asperi
Asperi

Reputation: 257533

Transition works when view is appeared in view hierarchy (not on screen), so to solve this we need another container and state.

Here is a fixed variant. Tested with Xcode 12.1 / iOS 14.1

demo

struct SheetContentView: View {
    @Binding var showSheet: Bool
    @State private var isShown = false
    var body: some View {
        VStack {            // container to animate transition !!
            if isShown {
                VStack(spacing: 8) {
                    Text("A great content of my new sheet")
                    Label("still not done", systemImage: "guitars")
                    Text("I'm done now")
                }
                .transition(.asymmetric(insertion: .scale, removal: .opacity))
            }
        }
        .animation(Animation.easeInOut(duration: 2))
        .onAppear {
            isShown = true       // << activate !!
        }
    }
}

Upvotes: 3

Related Questions