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

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

If you use the [`NavigationMapView`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationcore/navigationmapview/) or [`NavigationView`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/navigationview/) class for a free-drive navigation experience and [`NavigationViewController`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/navigationviewcontroller/) 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
class ViewController: UIViewController {

    var navigationView: NavigationView!
    let navigationProvider = MapboxNavigationProvider(coreConfig: CoreConfig())

    override func viewDidLoad() {
        super.viewDidLoad()

        navigationProvider.tripSession().startFreeDrive()

        navigationView = NavigationView(
            frame: view.bounds,
            mapViewConfiguration: .createNew(
                location: navigationProvider.navigation().locationMatching
                    .map(\.mapMatchingResult.enhancedLocation)
                    .eraseToAnyPublisher(),
                routeProgress: navigationProvider.navigation().routeProgress
                    .map(\.?.routeProgress)
                    .eraseToAnyPublisher(),
                predictiveCacheManager: navigationProvider.predictiveCacheManager
            )
        )
        navigationView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(navigationView)

        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)
        ])
        
        Task { [weak self] in
            guard let self = self else { return }
            
            do {
                let navigationRouteOptions = NavigationRouteOptions(coordinates: [
                    CLLocationCoordinate2D(latitude: 37.77766, longitude: -122.43199),
                    CLLocationCoordinate2D(latitude: 37.77536, longitude: -122.43494)
                ])
                
                // Request routes and present `NavigationViewController`.
                let navigationRoutes = try await navigationProvider.routingProvider().calculateRoutes(options: navigationRouteOptions).value
                
                let navigationViewController = NavigationViewController(
                    navigationRoutes: navigationRoutes,
                    navigationOptions: NavigationOptions(
                        mapboxNavigation: navigationProvider.mapboxNavigation,
                        voiceController: navigationProvider.routeVoiceController,
                        eventsManager: navigationProvider.eventsManager(),
                        styles: [NightStyle()],
                        predictiveCacheManager: navigationProvider.predictiveCacheManager
                    )
                )

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

                // Make sure to present `NavigationViewController` in animated way.
                self.present(navigationViewController, animated: true, completion: nil)
            } catch {
                print("Error occured while requesting routes: \(error.localizedDescription)")
            }
        }
    }
}

// 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`.
        toViewController.navigationMapView = fromViewController.navigationView.navigationMapView

        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

        transitionContext.containerView.addSubview(toViewController.view)
        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()
    }
}
```

## 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 make the required changes to this configuration when completing an activity change.

### `NavigationMapView` delegate

`NavigationViewController` changes the `NavigationMapView` delegate. If you use your own [`NavigationMapViewDelegate`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationcore/navigationmapviewdelegate/) implementation, make sure to reset it back when `NavigationViewController` is no longer active.

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

### 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/3.28.2/navigation/documentation/mapboxnavigationuikit/navigationviewcontroller/) and [`PreviewViewController`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/previewviewcontroller/), get the container view using the [`NavigationView.topBannerContainerView`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/navigationview/topbannercontainerview/) or [`NavigationView.bottomBannerContainerView`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/navigationview/bottombannercontainerview/) property, and call the container view's [`show(animated:duration:animations:completion:)`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/bannercontainerview/show(animated:duration:animations:completion:)/) or [`hide(animated:duration:animations:completion:)`](https://docs.mapbox.com/ios/navigation/api/3.28.2/navigation/documentation/mapboxnavigationuikit/bannercontainerview/hide(animated:duration:animations:completion:)/) method.