NewThe Flutter Kit — Flutter boilerplate$149$69
SwiftUI Tutorial · iOS 16+ · iOS 26 Liquid Glass

SwiftUI Sheet, Modal & Bottom Sheet — Complete 2026 Field Guide

Sheets, bottom sheets, half sheets, full-screen covers, custom modal transitions. All five SwiftUI presentation APIs in one production-ready reference.

Last updated: 2026-05-16 11 min read By Ahmed Gagan, iOS Engineer
Quick Answer

SwiftUI has five modal APIs: .sheet (slide-up partial), .fullScreenCover (full screen), .popover (anchored on iPad), .alert/.confirmationDialog (system dialogs), and the iOS 16+ .presentationDetents which converts a sheet into a resizable bottom sheet. For most apps in 2026, target iOS 16+ to use .presentationDetents([.medium, .large]) — it replaces every third-party bottom-sheet library and matches Apple's own UX (Maps, Wallet, AirDrop).

Min iOS
iOS 15 (basic) · iOS 16 (detents) · iOS 17 (interaction APIs)
Bottom sheet API
.presentationDetents([.medium, .large])
Dismiss control
.interactiveDismissDisabled(true)
Swift Kit support
✓ Paywall + onboarding sheets ready

Basic Sheet Presentation

Sheets work with two binding patterns. Boolean for "is this view showing", or Identifiable item for "show a sheet with this data". Prefer the item form when the sheet needs data to render — it avoids the optional unwrapping dance.

BasicSheet.swift
struct ContentView: View {
  @State private var selectedTask: Task?

  var body: some View {
    List(tasks) { task in
      Button(task.title) { selectedTask = task }
    }
    .sheet(item: $selectedTask) { task in
      TaskDetailView(task: task)
    }
  }
}

Bottom Sheets with .presentationDetents

The iOS 16+ presentationDetents modifier converts any sheet into a resizable bottom sheet. Pass an array of heights — users can drag between them. This is the same API that powers Maps and Wallet.

  • .medium = ~50% of screen, .large = full sheet
  • .fraction(0.3) for custom heights (iOS 16+)
  • .height(200) for fixed-point heights
  • .presentationDragIndicator(.visible) shows the grab handle
  • .presentationBackgroundInteraction(.enabled(upThrough: .medium)) lets users tap behind the sheet at smaller detents
BottomSheet.swift
.sheet(isPresented: $showFilters) {
  FiltersView()
    .presentationDetents([.fraction(0.3), .medium, .large])
    .presentationDragIndicator(.visible)
    .presentationBackgroundInteraction(.enabled(upThrough: .medium))
}

Full-Screen Covers (Onboarding, Paywalls)

.fullScreenCover takes over the entire window and requires programmatic dismissal. Use it for onboarding flows, paywalls, and any state where the user MUST complete or explicitly close. Sheets are for non-blocking UX.

PaywallCover.swift
.fullScreenCover(isPresented: $showPaywall) {
  PaywallView()
    .interactiveDismissDisabled(!hasFreeTrialEnded)
}

Common Bugs and Fixes

Three modal bugs you'll hit in production:

  • Sheet ignores @State changes — bind to @State on the parent, never inside the sheet content
  • Detents jump on first present — set .presentationDetents BEFORE the sheet first appears
  • Sheet inside NavigationStack loses navigation context — wrap the sheet content in its own NavigationStack
  • Keyboard pushes sheet content offscreen — use .presentationContentInteraction(.resizes) or scroll inside

Paywall and onboarding sheets — pre-built.

The Swift Kit ships production paywall sheets and 3-style onboarding bottom sheets wired to RevenueCat and the design system.

Get The Swift Kit — $99

Frequently Asked Questions

How do I present a sheet in SwiftUI?
Bind a Boolean or Identifiable item to .sheet(isPresented:content:) or .sheet(item:content:). The system handles the slide-up animation and dismissal swipe gesture automatically. Example: .sheet(isPresented: $showSettings) { SettingsView() }.
How do I create a bottom sheet in SwiftUI?
Use .presentationDetents([.medium, .large]) inside the sheet content. Add .presentationDragIndicator(.visible) for the iOS-style grabber. Available iOS 16+. For iOS 15 fallback, use a third-party library like SwiftUI BottomSheet.
What's the difference between sheet and fullScreenCover?
Sheets present partially (slide up, dismiss by swiping down). FullScreenCover takes over the entire screen and requires programmatic dismissal. Use sheet for non-blocking flows (settings, share), fullScreenCover for onboarding, paywalls, and required actions.
How do I disable interactive dismissal of a SwiftUI sheet?
Use .interactiveDismissDisabled(true) inside the sheet content. This blocks the swipe-down gesture but still allows programmatic dismissal. Useful for forms with unsaved changes or required steps.
How do I make a half-sheet that can resize?
Combine .presentationDetents with multiple values: .presentationDetents([.fraction(0.3), .medium, .large]). Users can drag to resize between detents. Add .presentationContentInteraction(.scrolls) so scrollable content works correctly inside.
Does The Swift Kit include sheet templates?
Yes — the Swift Kit includes pre-built paywall sheets, share sheets, settings sheets, and onboarding bottom sheets, all wired into the design system. Detent presets and dismiss-confirmation flows ship out of the box.

Keep exploring

Ship your iOS app 10× faster

The Swift Kit gives you a production-ready SwiftUI boilerplate — design system, paywall, auth, AI, all pre-wired. $99 one-time.

Get The Swift Kit — $99

One-time purchase · Lifetime updates · 14-day refund