> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/ios/ja/navigation/v2/llms.txt)

# Transition from free-drive to turn-by-turn navigation

If you use the [`NavigationMapView`](https://docs.mapbox.com/ios/navigation/api/2.21.0/Classes/NavigationMapView.html) or [`NavigationView`](https://docs.mapbox.com/ios/navigation/api/2.21.0/Turn-by-turn%20UI.html#/c:@M@MapboxNavigation@objc(cs)NavigationView) class for a free-drive navigation experience and [`NavigationViewController`](https://docs.mapbox.com/ios/navigation/api/2.21.0/Classes/NavigationViewController.html) for turn-by-turn navigation, you can seamlessly transition between these activities.

Since `NavigationMapView` and `NavigationView` are usually presented as a child view of a certain view controller, you can use a custom transition based on the [`UIViewControllerTransitioningDelegate`](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate).

## Requirements

To seamlessly transition between free-drive and turn-by-turn navigation sessions, your application needs to meet the following conditions:

-   When you present a `NavigationViewController`, set its [`modalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle) property to `fullScreen` and assign an object that conforms to the [`UIViewControllerTransitioningDelegate`](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate) protocol to its [`transitioningDelegate`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621421-transitioningdelegate) property.
-   Make sure that presentation of the `NavigationViewController` is animated. When calling the [`UIViewController.present(_:animated:completion:)`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-present) method, pass in `true` as the `animated` parameter.
-   Implement classes that conform to the [`UIViewControllerAnimatedTransitioning`](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning) protocol, which is used when presenting and dismissing `NavigationViewController` and takes care of reusing the `NavigationMapView` instance.

Consider the following example, where `NavigationView` is displayed on the surface of the `ViewController` instance, with `PresentationTransition` and `DismissalTransition` acting as custom animators:

```swift
import UIKit
import MapboxDirections
import MapboxCoreNavigation
import MapboxNavigation
import CoreLocation

class ViewController: UIViewController {
    private let routingProvider = MapboxRoutingProvider()
    var passiveLocationManager: PassiveLocationManager?

    var navigationView: NavigationView!

    func prepareFreeDriveMapView() {
        navigationView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(navigationView)
        passiveLocationManager = PassiveLocationManager()

        NSLayoutConstraint.activate([
            navigationView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            navigationView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            navigationView.topAnchor.constraint(equalTo: view.topAnchor),
            navigationView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        navigationView.navigationMapView.navigationCamera.follow()

        let navigationViewportDataSource = NavigationViewportDataSource(navigationView.navigationMapView.mapView,
                                                                        viewportDataSourceType: .passive)
        navigationView.navigationMapView.navigationCamera.viewportDataSource = navigationViewportDataSource
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationView = NavigationView(frame: view.bounds)
        prepareFreeDriveMapView()

        let navigationRouteOptions = NavigationRouteOptions(coordinates: [
            CLLocationCoordinate2D(latitude: 37.77766, longitude: -122.43199),
            CLLocationCoordinate2D(latitude: 37.77536, longitude: -122.43494)
        ])

        // Request a route and present `NavigationViewController`.
        routingProvider.calculateRoutes(options: navigationRouteOptions) { [weak self] result in
            switch result {
            case .failure(let error):
                print("Error occured: \(error.localizedDescription)")
            case .success(let routeResponse):
                guard let self = self else { return }

                let navigationService = MapboxNavigationService(indexedRouteResponse: routeResponse,
                                                                credentials: NavigationSettings.shared.directions.credentials,
                                                                simulating: .always)
                let navigationOptions = NavigationOptions(
                    navigationService: navigationService,
                    navigationMapView: navigationView.navigationMapView
                )

                let navigationViewController = NavigationViewController(for: routeResponse, navigationOptions: navigationOptions)
                navigationViewController.delegate = self

                // Make sure to set `transitioningDelegate` to be a current instance of `ViewController`.
                navigationViewController.transitioningDelegate = self

                // Make sure to present `NavigationViewController` in animated way.
                self.present(navigationViewController, animated: true, completion: nil)
            }
        }
    }
}

// Transition that is used for `NavigationViewController` presentation.
class PresentationTransition: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.0
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: .from) as? ViewController,
              let toViewController = transitionContext.viewController(forKey: .to) as? NavigationViewController else {
            transitionContext.completeTransition(false)
            return
        }

        // Re-use `NavigationMapView` instance in `NavigationViewController`.
        let navigationMapView = fromViewController.navigationView.navigationMapView
        toViewController.navigationMapView = navigationMapView

        // Clean up free drive location source
        fromViewController.passiveLocationManager = nil

        // Set active guidance location source
        let navigationViewportDataSource = NavigationViewportDataSource(navigationMapView.mapView,
                                                                        viewportDataSourceType: .active)
        navigationMapView.navigationCamera.viewportDataSource = navigationViewportDataSource
        transitionContext.containerView.addSubview(toViewController.view)
        transitionContext.completeTransition(true)
    }
}

// Transition that is used for `NavigationViewController` dismissal.
class DismissalTransition: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.0
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: .from) as? NavigationViewController,
              let navigationMapView = fromViewController.navigationMapView,
              let toViewController = transitionContext.viewController(forKey: .to) as? ViewController else {
            transitionContext.completeTransition(false)
            return
        }

        // Inject `NavigationMapView` instance that was previously used by `NavigationViewController` back to
        // `ViewController`.
        toViewController.navigationView.navigationMapView = navigationMapView
        toViewController.prepareFreeDriveMapView()

        // Set up free drive location
        toViewController.passiveLocationManager = PassiveLocationManager()
        navigationMapView.navigationCamera.viewportDataSource =
        NavigationViewportDataSource(navigationMapView.mapView, viewportDataSourceType: .passive)

        transitionContext.completeTransition(true)
    }
}

extension ViewController: UIViewControllerTransitioningDelegate {

    public func animationController(forPresented presented: UIViewController,
                                    presenting: UIViewController,
                                    source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return PresentationTransition()
    }

    public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DismissalTransition()
    }
}

extension ViewController: NavigationViewControllerDelegate {
    func navigationViewControllerDidDismiss(
        _ navigationViewController: NavigationViewController,
        byCanceling canceled: Bool
    ) {
        navigationViewController.dismiss(animated: true)
    }
}

```

## Injecting `NavigationMapView`

When transitioning between free-drive and a turn-by-turn navigation, the `NavigationViewController` and another view controller can share an instance of `NavigationMapView`. Since the `NavigationMapView` configuration can be completely different in these two activities, make sure to do the required changes to this configuration when completing an activity change.

### The navigation camera viewport's data source type

By default, whenever `NavigationViewController` is presented, it switches the navigation camera to see location updates in `RouteController.routeControllerProgressDidChange` notifications, by setting `NavigationViewportDataSource` to the `ViewportDataSourceType.active` type. After dismissing `NavigationViewController`, you need to switch to the desired viewport data source type:

```swift
navigationViewController.dismiss(animated: true) { [weak self] in
    guard let self = self else { return }

    let navigationMapView = self.viewController.navigationMapView

    let navigationViewportDataSource = NavigationViewportDataSource(navigationMapView.mapView,
                                                                    viewportDataSourceType: .passive)

    navigationMapView.navigationCamera.viewportDataSource = navigationViewportDataSource
}
```

### User location style

By default, `NavigationViewController` sets `NavigationMapView.userLocationStyle` to `UserLocationStyle.courseView(_:)` when the turn-by-turn navigation session starts. To preserve the user location style when transitioning from free-drive to turn-by-turn navigation:

```swift
viewController.present(navigationViewController,
                       animated: true,
                       completion: { [weak self] in
    guard let self = self else { return }

    navigationViewController.navigationMapView?.userLocationStyle = .puck2D()
}
```

### Banners

For a smooth transition between free-drive and turn-by-turn navigation sessions, you can show and hide the top and bottom banners with animation. Use the `navigationView` property of [NavigationViewController](https://docs.mapbox.com/ios/navigation/api/2.21.0/Classes/NavigationViewController.html) and [PreviewViewController](https://docs.mapbox.com/ios/navigation/api/2.21.0/Classes/PreviewViewController.html), get the container view using the `NavigationView.topBannerContainerView` or `NavigationView.bottomBannerContainerView` property, and call the container view's `show(animated:duration:animations:completion:)` or `hide(animated:duration:animations:completion:)` method.