TechnologiesSwiftUI

ManipulationGestureModifier struct

iOSmacOStvOSwatchOSvisionOS✓ renders

How it works

ManipulationGestureModifier is the view modifier that recognizes a combined pan, pinch, and rotate manipulation and feeds the resulting transform back into a view's geometry. Rather than wiring up separate DragGesture, MagnifyGesture, and RotateGesture recognizers and reconciling their simultaneous state by hand, you attach this single modifier and SwiftUI tracks the manipulation as one continuous interaction. Reach for it when you want a piece of content to feel directly handled — dragged, scaled, and spun under the user's fingers — with the platform managing gesture composition, inertia, and conflict resolution for you. You rarely name the type directly; you apply it through the manipulable() modifier, which returns a view wrapped in ManipulationGestureModifier.

  1. Apply the gesture with manipulable()

    The manipulable() modifier installs ManipulationGestureModifier on the view it decorates, opting that view into the unified pan/pinch/rotate recognizer. In the example it sits at the end of the chain on the RoundedRectangle, so the rectangle — already styled and sized — becomes the manipulable target. Because it returns a normal some View, it composes with the rest of the modifier chain like any other modifier.

  2. Order it after the transform you want to drive

    ManipulationGestureModifier reports its manipulation relative to where it is attached, so placement in the modifier chain matters. Here .manipulable() follows .scaleEffect(scale) and .frame(width:height:), meaning the gesture acts on the laid-out, scaled rectangle rather than on an un-transformed version of it. Inserting it before a transform would change which geometry the recognizer measures against.

  3. Reflect the manipulation in view state

    The manipulation produces a transform that you mirror into your own @State so the view re-renders as the user interacts. The example keeps an @State private var scale: CGFloat and binds it through .scaleEffect(scale); as the manipulation changes, the rectangle's scaleEffect updates, and the readout Text(String(format: "Scale: %.2f", scale)) shows the live value. This is the standard pattern: ManipulationGestureModifier drives the interaction, your state records the result.

  4. Let SwiftUI compose the sub-gestures

    A key reason to choose ManipulationGestureModifier over hand-assembled recognizers is that it treats translation, magnification, and rotation as facets of one gesture, so they can happen simultaneously without you arbitrating between them. The single .manipulable() call on the RoundedRectangle covers all three, where doing it manually would mean combining DragGesture, MagnifyGesture, and RotateGesture and resolving their overlap yourself.

Try it — Add .rotationEffect(angle) (backed by a new @State private var angle: Angle) to the RoundedRectangle alongside .scaleEffect(scale) to see ManipulationGestureModifier drive rotation as well as scale from the same .manipulable() interaction.

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.

ManipulationGestureModifier.swift
struct ManipulationGestureModifierDemo: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        VStack(spacing: 16) {
            Text("Manipulation Gesture")
                .font(.headline)
            RoundedRectangle(cornerRadius: 16)
                .fill(.blue.gradient)
                .frame(width: 120, height: 120)
                .scaleEffect(scale)
                .manipulable()
            Text(String(format: "Scale: %.2f", scale))
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
Manipulation Gesture
Manipulation Gesture
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →