Animations

Camera animations are the means by which camera settings are changed from old values to new values over a period of time. The Maps SDK for iOS provides expressive ways of controlling animations. You can animate the camera to move to a new center location and to update the bearing, pitch, zoom, padding, and anchor. Each of these camera properties can be animated independently through the Maps SDK camera animation system.

Note

Only one animation instance per camera setting type can run at a time. For example, if an animator that changes the camera's bearing is running, then starting another bearing operator at the same time will cancel the first one and then start running. This prevents the map from jumping between two or more animators of the same type. The code in the completion block will be called upon the animation's cancellation.

Animation types

The Maps SDK provides high-level and low-level animation APIs that allow you to transition the camera from old values to new values.

Using animations to change camera values

If you use MapboxMap.setCamera to change camera values instead of using an animator, the camera position will change abruptly to the new values instead of an animated change.

High-level animation APIs

The Maps SDK has a set of ready-to-use functions for animating map camera transitions. These high-level APIs allow you to implement animations quickly, but have limited options for customization. The Maps SDK provides a few built-in animation types:

  • fly(to:duration:completion:) changes the camera's position from the old values to the new values using a combination of zooming and panning in an animated transition that evokes flight.
  • ease(to:duration:completion:) gradually changes the camera's position with an animated transition from the old values to the new values.

It's important to note that only one high-level animation can run at a time. If a high-level animation starts while another high-level animation is already running, the running animation will be canceled even if the new high-level animation only changes map camera parameters the running one does not use. The code in the completion block will be called upon the animation's cancellation.

Low-level animation APIs

There are several lower-level animation APIs that you can use to implement animations with more customization options including running multiple animations simultaneously. These APIs offer more flexibility, but require writing more custom code.

The mapView.camera exposes a set of makeAnimator* functions that allow you to create other animations by setting new values for the camera position options. The following example shows how you could create an experience in which the map rotates and zooms at the same time:

var myCustomAnimator: BasicCameraAnimator = {
    mapView.camera.makeAnimator(duration: 5, curve: .easeInOut) { (transition) in
        
        // Transition the zoom level of the map's camera to `7`
        transition.zoom.toValue = 7
        
        // Transition the bearing of the map's camera to `180` degrees
        transition.bearing.toValue = 180
    }
}()

In the above scenario, transition is a CameraTransition object. This object is passed into every animation block, allowing you to control both where the animation completes (in other words, the final map camera) and its starting position.

Start a camera animation

Start a high-level animation event

Camera animations created using the high-level animations APIs start automatically when they are called. For example, the camera animation will start after you define new CameraOptions and call ease(to:). The animation takes five seconds:

// Center the map camera over New York City.
let centerCoordinate = CLLocationCoordinate2D(
    latitude: 40.7128, longitude: -74.0060)

let newCamera = CameraOptions(center: centerCoordinate,
                              padding: .zero,
                              anchor: .zero,
                              zoom: 7.0,
                              bearing: 180.0,
                              pitch: 15.0)

self.mapView.camera.ease(to: newCamera, duration: 5.0)
example
Use camera animations

Use ease(to:) to animate updates to the camera's position.

Start a low-level animation event

To start a camera animation created using the low-level animation APIs, use the BasicCameraAnimator.startAnimation method. For example, to start the animation defined in the low-level animation APIs section above:

let animator = mapView.camera.makeAnimator(duration: 4, curve: .easeOut) { (transition) in
  transition.zoom.toValue = 10
}

animator.startAnimation()
example
Use custom camera animations

Use the BasicCameraAnimator.startAnimation method to start new animations for several camera properties.

Chaining animations

BasicCameraAnimator's addCompletion supports completion blocks with the following API:

myCustomAnimator.addCompletion { position in
    print("Animation complete at position: \(position)")
}

These completion blocks can be used to chain animations to execute one after another, as in this example:

// Declare an animator that changes the map's
let bearingAnimator = mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in
  transition.bearing.toValue = -45
}

bearingAnimator.addCompletion { (_) in
  print("All animations complete!")
}

// Declare an animator that changes the map's pitch.
let pitchAnimator = mapView.camera.makeAnimator(duration: 2, curve: .easeInOut) { (transition) in
  transition.pitch.toValue = 55
}

// Begin the bearing animation once the pitch animation has finished.
pitchAnimator.addCompletion { _ in
  print("Animating camera bearing from 0 degrees -> 45 degrees")
  bearingAnimator.startAnimation()
}

// Declare an animator that changes the map's zoom level.
let zoomAnimator = self.mapView.camera.makeAnimator(duration: 4, curve: .easeInOut) { (transition) in
  transition.zoom.toValue = 14
}

// Begin the pitch animation once the zoom animation has finished.
zoomAnimator.addCompletion { _ in
  print("Animating camera pitch from 0 degrees -> 55 degrees")
  pitchAnimator.startAnimation()
}

// Begin the zoom animation.
zoomAnimator.startAnimation(afterDelay: 1)

Animation owner

AnimationOwner is a struct used to keep track of the source (or owner) for each animation. AnimationOwner has the following pre-defined values:

  • gestures: support for animations run by gestures
  • unspecified: support for a non-specific animation

Custom values can be created using AnimationOwner(rawValue:).

Listen for camera animation events

To check whether an animation is in progress, you can use MapEvents.cameraChanged. Note that this can also be called for camera changes triggered by gestures and can be called hundreds of times as the camera changes, so implementations using it should be lightweight.

mapView.mapboxMap.onEvery(.cameraChanged) { _ in
}

To check whether an individual animator is running, check if BasicCameraAnimator.isRunning.

To execute code after a camera animation is finished, use the camera animation's completion block.