Sheets are among the most common patterns in iOS apps, and yet most devs keep hand-rolling custom solutions. SwiftUI ships five presentation modifiers that handle sizing, indicators, backgrounds and adaptation, without ever going through UIKit.
.presentationDetents
Defines the height steps a sheet settles on as the user drags it. You pass an array of detent values and the system handles the whole snapping behaviour.
@State private var showSheet = false
Button("Show sheet") {
showSheet = true
}
.sheet(isPresented: $showSheet) {
SheetContent()
.presentationDetents([
.fraction(0.3),
.medium,
.large
])
}Three lines, three height steps. You can combine .medium, .large, .fraction(_:) and .height(_:) freely. The system handles the snap between each step: no custom gesture code, no manual frame maths. Available since iOS 16.
.presentationDragIndicator
Controls whether the small grabber shows at the top of the sheet. On .automatic, the default, SwiftUI hides it when there is a single detent, since there is nothing to move between. You force it with .visible when you want to signal that the sheet can be swiped away.
.sheet(isPresented: $show) {
VStack {
Text("Pull me down")
Text("or swipe to dismiss")
}
.presentationDetents([.medium])
.presentationDragIndicator(.visible)
}The grabber is an affordance cue. Without it, nothing tells the user the sheet can be resized or dismissed with a swipe. One word, zero design work. Available since iOS 16.
.presentationSizing(.fitted)
Sizes the sheet automatically to hug its content. No detents needed for small confirmations, toasts or pickers: iOS measures the content and adapts the sheet to it.
.sheet(isPresented: $show) {
VStack(spacing: 14) {
Image(systemName: "checkmark")
Text("Success")
Button("OK") { show = false }
}
.padding(28)
.presentationSizing(.fitted)
}That kills the half-screen modal shown for a single button or a one-line message. The other sizing options are .automatic, .page and .form. Available since iOS 18.
.presentationCompactAdaptation
On iPad, popovers stay popovers. On iPhone, SwiftUI turns them into sheets by default. This modifier lets you override that. Pass .none to keep a popover as a real popover in compact size classes.
Button("Filter") {
showPopover = true
}
.popover(isPresented: $showPopover) {
VStack(alignment: .leading) {
Text("Newest")
Text("Oldest")
Text("Most popular")
}
.padding()
.presentationCompactAdaptation(.none)
}Without .none, the popover becomes a sheet on iPhone: a half-screen modal just to show three filter options. With .none, it stays anchored to the button, small and contextual. The other options are .sheet and .fullScreenCover. Available since iOS 16.4.
.presentationBackground
Replaces the sheet's default white background with a material, a solid colour or any SwiftUI view. This is how you get translucent sheets without reaching for UIKit.
.sheet(isPresented: $show) {
SheetContent()
.presentationDetents([.medium])
.presentationBackground(.ultraThinMaterial)
}You can also pass a view builder for full control:
.presentationBackground {
LinearGradient(
colors: [.cyan, .blue],
startPoint: .top,
endPoint: .bottom
)
}Material backgrounds let the content behind the sheet show through, which helps on map overlays, media pickers, and any context where the user needs to see what sits underneath. Available since iOS 16.4.
The takeaway
Detents handle the height steps, the drag indicator signals interactivity, fitted sizing kills the oversized modal problem, compact adaptation keeps popovers honest on iPhone, and presentation backgrounds unlock translucency. Stop hacking your sheets together, let the system do the work!

Commentaires
Aucun commentaire pour le moment. Sois le premier !