Skip to content

MapKit Interactions (SwiftUI)

A SwiftUI guide for coding agents. Also covers onmapcamerachange, track map drag ios, map camera keyframe animation, map marker selection binding, fly through map animation.

Three iOS 17 MapKit modifiers cover map interactivity: onMapCameraChange observes the camera while the user drags, mapCameraKeyframeAnimator animates the camera along keyframes, and the selection binding makes markers selectable.

Observing the Camera

Watching the position binding with onChange does not report the camera during a user drag. Use onMapCameraChange: its closure receives a MapCameraUpdateContext with the current camera, region, and rect.

Map(position: $position) { /* markers */ }
  .onMapCameraChange(frequency: .continuous) { context in
    print(context.camera)
    print(context.region)
    print(context.rect)
  }

MapCameraUpdateFrequency has two cases: continuous reports in near real time, onEnd fires once when the drag finishes. Pick onEnd for expensive work such as refetching pins.

Animating the Camera

mapCameraKeyframeAnimator runs a keyframe animation on the camera whenever its trigger value changes. Every MapCamera property is animatable through KeyframeTrack, and the builder hands you the camera's value before the animation starts.

.mapCameraKeyframeAnimator(trigger: trigger) { camera in
  KeyframeTrack(\MapCamera.centerCoordinate) {
    LinearKeyframe(.newYork, duration: 2)
    LinearKeyframe(.seattle, duration: 2)
    LinearKeyframe(.sanFrancisco, duration: 2)
  }

  KeyframeTrack(\MapCamera.distance) {
    LinearKeyframe(camera.distance, duration: 2)
    LinearKeyframe(camera.distance * 2, duration: 2)
    LinearKeyframe(camera.distance, duration: 2)
  }
}

Selecting Markers

Pass a selection binding to the Map initializer and tag each marker. The selection type must match the tag type, or nothing selects.

@State private var selection: Int?

Map(position: $position, selection: $selection) {
  Marker("New York", monogram: Text("NY"), coordinate: .newYork).tag(1)
  Marker("Seattle", monogram: Text("SE"), coordinate: .seattle).tag(2)
  Marker("San Francisco", monogram: Text("SF"), coordinate: .sanFrancisco).tag(3)
}
.onChange(of: selection) {
  print("selection changed:", selection as Any)
}

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-mastering-mapkit-interactions" })
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