Skip to content

Bottom Sheet API (SwiftUI)

A SwiftUI guide for coding agents. Also covers presentationdetents half sheet, resizable sheet ios, custom detent height, sheet drag indicator, interact behind sheet, sheet corner radius background.

Since iOS 16, a sheet becomes a bottom sheet by attaching presentationDetents to the sheet's content. The API covers sizing, programmatic resize, drag indicator, background, and interaction behavior, so hand-rolled bottom sheets are rarely needed.

Detents Set the Sizes

One detent fixes the size; several let the user drag between them. .medium is half the screen.

.sheet(isPresented: $sheetShown) {
  NavigationStack {
    Text("Your query: \(query)")
      .searchable(text: $query)
      .navigationTitle("Search")
  }
  .presentationDetents([.medium, .large])
}

.fraction(0.2) sizes by a fraction of available space, .height(300) by points, and the options mix freely in one array.

Custom Detents Read the Environment

Conform to CustomPresentationDetent for sizing logic that depends on context. The Context exposes environment values, such as the dynamic type size.

struct MyCustomDetent: CustomPresentationDetent {
  static func height(in context: Context) -> CGFloat? {
    if context.dynamicTypeSize.isAccessibilitySize {
      return context.maxDetentValue
    } else {
      return context.maxDetentValue * 0.8
    }
  }
}

.presentationDetents([.medium, .custom(MyCustomDetent.self)])

Programmatic Resize

Pass a selection binding and the sheet follows it, in both directions: dragging updates the binding, and setting the binding resizes the sheet.

@State private var sheetSize: PresentationDetent = .medium

.presentationDetents([.medium, .large], selection: $sheetSize)

Appearance and Interaction Modifiers

Each of these attaches to the sheet content, next to presentationDetents:

.presentationDragIndicator(.hidden)
.presentationCornerRadius(16)
.presentationBackground(Material.ultraThin)
.presentationContentInteraction(.scrolls)
.presentationBackgroundInteraction(.enabled(upThrough: .large))

Gotchas worth knowing: when the sheet holds a scroll view, the first drag resizes the sheet by default, so set .presentationContentInteraction(.scrolls) to scroll first. The content behind a sheet is blocked by default; presentationBackgroundInteraction re-enables it up to a chosen detent, the map-app pattern. presentationBackground also takes a ViewBuilder closure for a fully custom background view.

Use this guidance in your coding agent

Install the Better Design MCP once. Your agent then loads this page with one call.

get-swiftui-guide({ topic: "swiftui-bottom-sheet-api" })
claude mcp add --scope user better-design --transport http https://better-design.com/api/mcp --header "Authorization: Bearer <YOUR_API_KEY>"
Browse related design systems