TechnologiesSwiftUIPresentation and Dialogs

PresentationContentInteraction struct

iOSmacOStvOSwatchOSvisionOSiOS 16.4+✓ renders

A behavior that you can use to influence how a presentation responds to

How it works

PresentationContentInteraction is a set of constants that tells SwiftUI how to resolve a gesture conflict inside a resizable presentation. When a sheet has multiple detents and its content scrolls, a swipe is ambiguous: it could either resize the sheet or scroll the content. This type lets you declare which behavior wins, so you reach for it whenever a scrollable sheet feels like it's fighting the user's drag. You apply one of its values through the presentationContentInteraction(_:) modifier rather than constructing it directly.

  1. Give the sheet resizable detents first

    The interaction setting only matters when the presentation can change size, so it presumes a set of presentationDetents. In the example the sheet declares [.medium, .large], which is exactly the situation where a drag is ambiguous between resizing to .large and scrolling the content.

  2. Apply the policy with presentationContentInteraction(_:)

    Attach presentationContentInteraction(_:) to the content inside the sheet's closure, passing the constant you want. Here .presentationContentInteraction(.scrolls) sits alongside .presentationDetents(...) so the rule travels with this specific presentation.

  3. Prefer scrolling with .scrolls

    The .scrolls value makes a swipe move the scrollable content first; the sheet only resizes once scrolling can't continue (for example, when the content is already at the top and the user keeps dragging down). The ScrollView of rows in the example responds to drags immediately rather than snapping the sheet to a new detent.

  4. Prefer resizing with .resizes

    The .resizes value is the inverse: a swipe changes the sheet's detent first and only scrolls the content once the largest detent is reached. This is the default-feeling behavior, and it's the constant you swap in when the sheet itself, not its content, should own the gesture.

  5. Treat it as a value, not a constructed object

    PresentationContentInteraction has no initializer you call yourself; you choose among the static members .scrolls and .resizes and hand the result to the modifier. Because it's a plain value type, the policy is fixed for the lifetime of the presentation it's attached to.

Try it — Change .presentationContentInteraction(.scrolls) to .presentationContentInteraction(.resizes) and drag up on the rows: instead of scrolling, the sheet now expands from .medium to .large before the content moves.

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

PresentationContentInteraction.swift
struct PresentationContentInteractionDemo: View {
    @State private var showing = true

    var body: some View {
        Color.clear
            .sheet(isPresented: $showing) {
                ScrollView {
                    VStack(spacing: 12) {
                        ForEach(0..<20) { i in
                            Text("Row \(i)")
                                .frame(maxWidth: .infinity)
                                .padding()
                                .background(.quaternary)
                        }
                    }
                    .padding()
                }
                .presentationDetents([.medium, .large])
                .presentationContentInteraction(.scrolls)
            }
            .padding()
    }
}
Live preview
Row 0 Row 1 Row 2 Row 3 Row 4 Row 5 Row 6 Row 7 Row 8 Row 9 Row 10 Row 11 Row 12 Row 13 Row 14 Row 15 Row 16 Row 17 Row 18 Row 19
Row 0 Row 1 Row 2 Row 3 Row 4 Row 5 Row 6 Row 7 Row 8 Row 9 Row 10 Row 11 Row 12 Row 13 Row 14 Row 15 Row 16 Row 17 Row 18 Row 19
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →