Skip to content

Content Shape (SwiftUI)

A SwiftUI guide for coding agents. Also covers contentshape modifier, hit testing shape, drag preview outline, hover effect shape ipados, contextmenu preview shape, tap area larger.

The contentShape view modifier changes the shape a view uses for interactions: hit-testing, drag previews, context menu previews, and the iPadOS hover effect. It never changes how the view renders; that stays with clipShape.

Scoping a Shape to an Interaction

Pass a ContentShapeKinds value plus any Shape. Here the hover effect becomes a circle while the view still draws as a rectangle.

struct ContentView: View {
  var body: some View {
    VStack {
      Image(systemName: "star")
      Text("Hello World!")
    }
    .contentShape(.hoverEffect, Circle())
    .hoverEffect()
    .onTapGesture {
      print("Super star!")
    }
  }
}

The available kinds:

  1. interaction sets the hit-testing shape.
  2. dragPreview sets the outline for drag and drop previews.
  3. contextMenuPreview sets the shape of context menu previews.
  4. hoverEffect sets the iPadOS pointer hover shape.

ContentShapeKinds is an OptionSet, so one call can cover several interactions:

.contentShape([.hoverEffect, .dragPreview], Circle())

Custom Shapes

Any Shape works, including your own paths. A custom triangle clips the drag preview without touching the rendered view.

struct Triangle: Shape {
  func path(in rect: CGRect) -> Path {
    var path = Path()
    path.move(to: CGPoint(x: rect.minX, y: rect.maxY))
    path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
    path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
    return path
  }
}

VStack {
  Image(systemName: "star")
  Text("Hello World!")
}
.contentShape(.dragPreview, Triangle())

Keep the two modifiers straight: contentShape is for interaction geometry, clipShape is for rendering. A stack with spacing also needs contentShape(Rectangle()) before a tap gesture if the gaps between children should be tappable.

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-customizing-view-content-shape" })
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