TechnologiesSwiftUI

ManipulationGeometryModifier struct

iOSmacOStvOSwatchOSvisionOS✓ renders

How it works

ManipulationGeometryModifier is the modifier type SwiftUI produces when you give a view interactive manipulation behavior, threading a live geometric transform — scale, rotation, and translation — into the view's layout as the user pinches, rotates, or drags. Rather than wiring each gesture to its own state and effect by hand, it consolidates the running transform so the view's geometry tracks the manipulation in one place. Reach for it when a piece of content should feel directly manipulable, like a photo, sticker, or card the user can resize and reposition under their fingers, and you want the geometry to update continuously while the gesture is in flight and resolve cleanly when it ends.

  1. Bind the live transform to state

    The modifier feeds the manipulation's current geometry back into your view's state so each frame reflects the latest input. Here that state is a single @State private var scale: CGFloat = 1.0, the value the geometry reads from as it changes — the starting 1.0 is the identity transform, the view's unmanipulated size.

  2. Apply the running scale to the view's geometry

    The transform is expressed against the view through a geometry effect, which is exactly the surface ManipulationGeometryModifier drives. The example uses .scaleEffect(scale) so the blue card grows and shrinks with the bound value; rotation and translation plug in the same way when a manipulation supplies them.

  3. Drive the geometry from a manipulation gesture

    Manipulation geometry is produced by a recognizer attached with .gesture(...). Here a MagnifyGesture() is the source of the transform, and ManipulationGeometryModifier is what relays its output into the view's layout instead of you computing frames yourself.

  4. Track the in-flight value with onChanged

    While the gesture is active the modifier updates the geometry continuously. The .onChanged { scale = $0.magnification } closure assigns the gesture's running magnification to the bound state, so the card resizes smoothly the entire time the user is pinching rather than only at the end.

  5. Resolve the geometry when the gesture ends

    When the manipulation completes you decide whether the transform persists or returns to identity. The .onEnded { _ in scale = 1.0 } closure resets the bound value to 1.0, snapping the geometry back to its resting state; dropping this reset is what lets a manipulation commit its final transform instead.

Try it — In .onEnded, replace scale = 1.0 with a no-op (or scale = $0.magnification) so the card keeps the pinched size after you lift your fingers, revealing that the modifier holds whatever geometry the manipulation last produced.

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.

ManipulationGeometryModifier.swift
struct ManipulationGeometryModifierDemo: View {
    @State private var scale: CGFloat = 1.0

    var body: some View {
        VStack(spacing: 16) {
            Text("Manipulate Me")
                .font(.headline)
                .padding()
                .frame(width: 160, height: 100)
                .background(.blue.gradient, in: .rect(cornerRadius: 16))
                .foregroundStyle(.white)
                .scaleEffect(scale)
                .gesture(
                    MagnifyGesture()
                        .onChanged { scale = $0.magnification }
                        .onEnded { _ in scale = 1.0 }
                )

            Text("Pinch to manipulate geometry")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
    }
}
Live preview
Manipulate Me Pinch to manipulate geometry
Manipulate Me Pinch to manipulate geometry
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →