Building a draggable bottom sheet in SwiftUI
Learn how to build a draggable bottom sheet using presentation detents.
20 Apr 2026 · 5 min read
Some apps, such as Apple Maps or Google Maps, use a draggable bottom sheet to let users switch between the main content, for example a map, and additional information presented in a sheet.
In this article, we'll look at how to build a draggable bottom sheet that works together with a scroll view and still allows interaction with the underlying content.
Let's jump in.

Implementing the bottom sheet
To be more precise, what we'd like to achieve is:
- the sheet has multiple states such as collapsed, medium and expanded
- the sheet is not fully dismissable by user interaction
- the user can interact with the contents of the underlying content such as a map
- dragging the sheet works together with a scroll view used to display its content
We can implement the described behaviour by using SwiftUI's sheet combined with presentationDetents:
struct ContentView: View {@State private var showingBottomSheet = truevar body: some View {MapView().sheet(isPresented: $showingBottomSheet) {ScrollView {// Add content of the bottom sheet here.}.interactiveDismissDisabled().presentationDetents([.height(50), .medium, .large]).presentationBackgroundInteraction(.enabled(upThrough: .large))}}}
Let's break this down.
Understanding detents
Detents define the different heights the sheet can snap to.
.presentationDetents([.height(50), .medium, .large])
In this example:
- .height(50) represents a collapsed state
- .medium is a partially expanded state
- .large expands the sheet to full height
By combining these, we create a sheet that can move between multiple states depending on the user's interaction.
Controlling dismissal behavior
By default, sheets can be dismissed by dragging them down. In this case, we don't want that default behavior:
.interactiveDismissDisabled()
This ensures that the sheet stays visible and behaves more like a persistent UI element rather than a modal.
Enabling background interaction
One important aspect of the sheet is that the user can still interact with the underlying content, for example a map.
.presentationBackgroundInteraction(.enabled(upThrough: .large))
This allows interaction with the content behind the sheet while it's not fully covering it.
Working with scroll views
To display content inside the sheet, we typically use a ScrollView.
SwiftUI automatically coordinates the scrolling behavior with the sheet’s dragging behavior:
- when the scroll view is scrolled, it handles the gesture
- when it reaches the top, dragging continues to move the sheet
This coordination is built into the system and does not require additional steps from our side.
Adding a drag indicator
To make the interaction more discoverable, we can show a drag indicator:
.presentationDragIndicator(.visible)
This provides a visual hint that the sheet can be dragged.
Limitations
One limitation of this approach is that the sheet is always presented above the entire view hierarchy.
This means it will also cover elements such as a tab bar. If more control over layering is required, for example to place content above the sheet, a custom implementation may be necessary.



Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips



