Skip to content

Instantly share code, notes, and snippets.

@simonbs
Last active December 2, 2025 10:03
Show Gist options
  • Select an option

  • Save simonbs/3ef87ba29e6eca34b919cbdc5f346aae to your computer and use it in GitHub Desktop.

Select an option

Save simonbs/3ef87ba29e6eca34b919cbdc5f346aae to your computer and use it in GitHub Desktop.
Is there a reliable way in SwiftUI to show a sheet without animation so it is visible immediately when the parent view appears?
struct ContentView: View {
@State private var isModalPresented = false
var body: some View {
VStack {
Button {
isModalPresented = true
} label: {
Text("Present Modal")
}
}
.padding()
.sheet(isPresented: $isModalPresented) {
ModalView()
}
}
}
struct ModalView: View {
@Environment(\.dismiss) private var dismiss
@State private var isSheetPresented = true
var body: some View {
NavigationView {
VStack {
Text("I'm presented modally.")
Spacer()
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(uiColor: .systemGroupedBackground))
.navigationTitle("Modal")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button {
dismiss()
} label: {
Label("Close", systemImage: "xmark")
}
}
}
// How can the sheet be shown without animation, so it's already visible when the modal opens? πŸ‘‡πŸ€”
.sheet(isPresented: $isSheetPresented) {
Text("I am a sheet. How can I be shown without animation, so I am already visible when the modal opens?")
.padding()
.presentationDetents([.medium])
.presentationBackgroundInteraction(.enabled)
.presentationDragIndicator(.hidden)
.interactiveDismissDisabled(true)
}
}
}
}
@fatbobman
Copy link

struct ContentView: View {
    @State private var isModalPresented = false
    @State private var isSheetPresented = true
    @State private var isSecondSheetPresented = true

    var body: some View {
        VStack {
            Button {
                var transaction = Transaction(animation: .none)
                // disable animation
                transaction.disablesAnimations = true
                withTransaction(transaction) {
                    isModalPresented = true
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.01){
                    withTransaction(transaction) {
                        isSheetPresented = true
                    }
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.02){
                    withTransaction(transaction) {
                        isSecondSheetPresented = true
                    }
                }

            } label: {
                Text("Present Modal")
            }
        }
        .padding()
        .sheet(isPresented: $isModalPresented) {
            ModalView(isSheetPresented: $isSheetPresented,isSecondSheetPresented: $isSecondSheetPresented)
        }
    }
}

struct ModalView: View {
    @Environment(\.dismiss) private var dismiss
    @Binding var isSheetPresented: Bool
    @Binding var isSecondSheetPresented: Bool

    var body: some View {
        NavigationView {
            VStack {
                Text("I'm presented modally.")
                Spacer()
            }
            .padding()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color(uiColor: .systemGroupedBackground))
            .navigationTitle("Modal")
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .topBarLeading) {
                    Button {
                        dismiss()
                    } label: {
                        Label("Close", systemImage: "xmark")
                    }
                }
            }
            // How can the sheet be shown without animation, so it's already visible when the modal
            // opens? πŸ‘‡πŸ€”
            .sheet(isPresented: $isSecondSheetPresented) {
                Text(
                    "I am a sheet. How can I be shown without animation, so I am already visible when the modal opens?")
                    .padding()
                    .presentationDetents([.medium])
                    .presentationBackgroundInteraction(.enabled)
                    .presentationDragIndicator(.hidden)
                    .interactiveDismissDisabled(true)
            }
        }
    }
}

@Codelaby
Copy link

Codelaby commented Dec 1, 2025

Combine two sheets: a main modal sheet with animation, and a second (secondary) sheet without animation. However, it needs a delay for the transition to finish; apparently, onAppear executes when the transition of the parent sheet ends.

struct ModalViewDemo: View {
    @State private var isModalPresented = false
    
    var body: some View {
        VStack {
            Button {
                isModalPresented = true
            } label: {
                Text("Present Modal")
            }
        }
        .padding()
        .sheet(isPresented: $isModalPresented) {
            ModalView()
            
        }
        
    }
}

struct ModalView: View {
    @Environment(\.dismiss) private var dismiss
    @State private var isSheetPresented = true
    @State private var isLoading = true
    
    var body: some View {
        if isLoading {
            ProgressView()
                .task {
                    try? await Task.sleep(for: .seconds(0.5))
                    withAnimation(.smooth) {
                        isLoading = false
                    }
                }
        } else {
            NavigationView {
                VStack {
                    Text("I'm presented modally.")
                    Spacer()
                    
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(Color.gray.quinary)
                .navigationTitle("Modal")
                .navigationBarTitleDisplayMode(.inline)
                .toolbar {
                    ToolbarItem(placement: .cancellationAction) {
                        Button(role: .close) {
                            dismiss()
                        }
                    }
                }
                // How can the sheet be shown without animation, so it's already visible when the modal opens? πŸ‘‡πŸ€”
                .sheet(isPresented: $isSheetPresented) {
                    Text("I am a sheet. How can I be shown without animation, so I am already visible when the modal opens?")
                        .padding()
                        .presentationDetents([.medium])
                        .presentationBackgroundInteraction(.enabled)
                        .presentationDragIndicator(.hidden)
                        .interactiveDismissDisabled(true)
                }
                .transaction { transaction in
                    transaction.disablesAnimations = true
                }
            }
        }
    }
}

#Preview {
    ModalViewDemo()
}

Only one sheet

// MARK: Utils
fileprivate extension View {
    func onFirstAppear(perform action: @escaping () -> Void) -> some View {
        modifier(ViewFirstAppearModifier(perform: action))
    }
}

fileprivate struct ViewFirstAppearModifier: ViewModifier {
    @State private var didAppearBefore = false
    private let action: () -> Void
    
    init(perform action: @escaping () -> Void) {
        self.action = action
    }
    
    func body(content: Content) -> some View {
        content.onAppear {
            guard !didAppearBefore else { return }
            didAppearBefore = true
            action()
        }
    }
}

// MARK: Demo
struct ModalViewDemo: View {
    @State private var isModalPresented = false
    
    var body: some View {
        NavigationView {
            VStack {
                Text("I'm presented modally.")
                Button("open sheet") {
                    isModalPresented = true
                }
                Spacer()
            }
            .padding()
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(.gray.quinary)
            .navigationTitle("Modal")
            .navigationBarTitleDisplayMode(.inline)
            
            .sheet(isPresented: $isModalPresented) {
                NavigationStack {
                    VStack {
                        Text("this is a modal sheet")
                            .padding()
                            .presentationDetents([.medium])
                            .presentationBackgroundInteraction(.enabled)
                            .presentationDragIndicator(.hidden)
                            .interactiveDismissDisabled(true)
                    }
                    .toolbar {
                        ToolbarItem(placement: .cancellationAction) {
                            Button(role: .close) {
                                isModalPresented.toggle()
                            }
                        }
                    }
                    
                }
            }
            .onFirstAppear {
                var transaction = Transaction(animation: .none)
                transaction.disablesAnimations = true
                withTransaction(transaction) {
                    isModalPresented = true
                }
            }

        }
    }
}

#Preview {
    ModalViewDemo()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment