zumzum
zumzum

Reputation: 20138

SwiftUI: fullScreenCover with no animation?

I have this view:

struct TheFullCover: View {
    
    @State var showModal = false
    
    var body: some View {
        
        
        Button(action: {
            showModal.toggle()
        }) {
            Text("Show Modal")
                .padding()
                .foregroundColor(.blue)
            
        }
        .background(Color(.white))
        .overlay(
            RoundedRectangle(cornerRadius: 10)
                .stroke(.red, lineWidth:1)
        )
        .fullScreenCover(isPresented: $showModal, onDismiss: {
            
        }, content: {
            VStack {
                Text("Here I am")
                TheFullCover()
            }
        })
        
    }
    
    
}

Every time I press the Button, the modal screen comes up fullscreen. All works great.

Question:

How do I disable the slide up animation? I want the view to be presented immediately fullscreen without animating to it.

Is there a way to do that?

Upvotes: 21

Views: 21664

Answers (6)

Imanou Petit
Imanou Petit

Reputation: 92409

You can disable animations when presenting or dismissing a View by wrapping the state / binding mutation in withTransaction(_:_:) and setting the transaction's disablesAnimations property to true.

The following Swift 5.9 / iOS 17 example shows a possible implementation to disable animations when a View is presented with fullScreenCover or dismissed:

import SwiftUI

struct ContentView: View {
    @State private var isPresentingFullScreenView = false

    var body: some View {
        NavigationStack {
            Form {
                Button("Present (with animation)") {
                    isPresentingFullScreenView = true
                }
                Button("Present (no animation)") {
                    var transaction = Transaction()
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        isPresentingFullScreenView = true
                    }
                }
            }
            .fullScreenCover(isPresented: $isPresentingFullScreenView) {
                FullScreenView(isPresentingFullScreenView: $isPresentingFullScreenView)
            }
            .navigationTitle("Content view")
        }
    }
}
import SwiftUI

struct FullScreenView: View {
    @Binding var isPresentingFullScreenView: Bool

    var body: some View {
        NavigationStack {
            Form {
                Button("Dismiss (with animation)") {
                    isPresentingFullScreenView = false
                }
                Button("Dismiss (no animation)") {
                    var transaction = Transaction()
                    transaction.disablesAnimations = true
                    withTransaction(transaction) {
                        isPresentingFullScreenView = false
                    }
                }
            }
            .navigationTitle("Full screen view")
        }
    }
}

Upvotes: 3

chitgoks
chitgoks

Reputation: 310

Why not use an overlay instead?

.overlay {
        if isLoading {
            ZStack {
                ProgressView()
            }
            .background(BackgroundCleanerView())
        }
    }

Upvotes: -3

Asperi
Asperi

Reputation: 257493

A possible solution is to disable views animation completely (and then, if needed, enable again in .onAppear of presenting content), like

    Button(action: {
        UIView.setAnimationsEnabled(false)    // << here !!
        showModal.toggle()
    }) {

and then

    }, content: {
        VStack {
            Text("Here I am")
            TheFullCover()
        }
        .onAppear {
            UIView.setAnimationsEnabled(true)    // << here !!
        }
    })

Tested with Xcode 13 / iOS 15

Upvotes: 23

asamoylenko
asamoylenko

Reputation: 2455

AFAIK the proper to do it as of today is using transaction https://developer.apple.com/documentation/swiftui/transaction

var transaction = Transaction()
transaction.disablesAnimations = true
withTransaction(transaction) {
   showModal.toggle()
}

I also created a handy extension for this:

extension View {
    func withoutAnimation(action: @escaping () -> Void) {
        var transaction = Transaction()
        transaction.disablesAnimations = true
        withTransaction(transaction) {
            action()
        }
    }
}

which can be used like this:

withoutAnimation {
    // do your thing
}

Upvotes: 40

congnd
congnd

Reputation: 1274

At the moment, I find it easier to use UIKit for presentation in SwiftUI.

someView
  .onChange(of: isPresented) { _ in
    if isPresented {
      let vc = UIHostingController(rootView: MyView())
      vc.modalPresentationStyle = .overFullScreen
      UIApplication.shared.rootVC?.present(vc, animated: false)
    } else {
      UIApplication.shared.rootVC?.dismiss(animated: false)
    }
  }

Upvotes: 8

Bruno Fulber Wide
Bruno Fulber Wide

Reputation: 502

.fullScreenCover(isPresented: isPresented) {
    content()
        .background(TransparentBackground())
}
.transaction({ transaction in
    transaction.disablesAnimations = true
})

this should work, based on @asamoylenko's answer

Upvotes: 19

Related Questions