Skip to main content

Adjust the Map's Viewport

This example demonstrates the usage of viewport provided by the Mapbox Maps SDK for iOS. Viewport is used to support camera synchronization with the puck, display an overview of a region, and switch between the two states using transitions. The example includes logic to handle two states: following the puck and displaying an overview. By toggling between the following and overview states, users can experience different viewpoints within the map. It is recommended to try this example in a simulator by selecting Features > Location > Freeway Drive to see the resulting user experience.

The example utilizes classes such as FollowPuckViewportState for the following state and OverviewViewportState for the overview state, each configured with specific options. Additionally, the implementation involves observing changes in the viewport status through the ViewportStatusObserver protocol, providing insights into viewport status transitions.

iOS Examples App Available

This example code is part of the Maps SDK for iOS Examples App, a working iOS project available on Github. iOS developers are encouraged to run the examples app locally to interact with this example in an emulator and explore other features of the Maps SDK.

See our Run the Maps SDK for iOS Examples App tutorial for step-by-step instructions.

ViewportExample.swift
import UIKit
import MapboxMaps

// This example shows how to use the viewport API to keep the camera in sync with the puck,
// show an overview of a region, and toggle between those states with transitions.
//
// When trying this example in the simulator, choose Features > Location > Freeway Drive
// to get a good sense of the resulting user experience.
final class ViewController: UIViewController {
private enum State {
case following
case overview
}

private var state: State = .following {
didSet {
syncWithState()
}
}

private var viewportButton = UIButton(type: .system)
private var mapView: MapView!
private var followPuckViewportState: FollowPuckViewportState!
private var overviewViewportState: OverviewViewportState!

override func viewDidLoad() {
super.viewDidLoad()

mapView = MapView(frame: view.bounds)
view.addSubview(mapView)

let cupertino = CLLocationCoordinate2D(latitude: 37.3230, longitude: -122.0322)

mapView.mapboxMap.setCamera(to: CameraOptions(center: cupertino, zoom: 14))

mapView.location.options.puckType = .puck2D(.makeDefault(showBearing: true))
mapView.location.options.puckBearing = .heading
mapView.location.options.puckBearingEnabled = true

followPuckViewportState = mapView.viewport.makeFollowPuckViewportState(
options: FollowPuckViewportStateOptions(
bearing: .heading))

overviewViewportState = mapView.viewport.makeOverviewViewportState(
options: OverviewViewportStateOptions(
geometry: Polygon(
center: cupertino,
radius: 20000,
vertices: 100)))

viewportButton.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 13.0, *) {
viewportButton.backgroundColor = .systemBackground
} else {
viewportButton.backgroundColor = .white
}
viewportButton.contentEdgeInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
view.addSubview(viewportButton)
NSLayoutConstraint.activate([
viewportButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
viewportButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)])
viewportButton.addTarget(
self,
action: #selector(toggleViewportState),
for: .touchUpInside)

syncWithState()
}



override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// break strong reference cycle
mapView.viewport.removeStatusObserver(self)
}

@objc private func toggleViewportState() {
switch state {
case .overview:
state = .following
case .following:
state = .overview
}
}

private func syncWithState() {
switch state {
case .following:
mapView.viewport.transition(to: followPuckViewportState)
viewportButton.setTitle("Overview", for: .normal)
case .overview:
mapView.viewport.transition(to: overviewViewportState)
viewportButton.setTitle("Follow", for: .normal)
}
}
}

extension ViewController: ViewportStatusObserver {
func viewportStatusDidChange(
from fromStatus: ViewportStatus,
to toStatus: ViewportStatus,
reason: ViewportStatusChangeReason) {
print("Viewport.status changed\n from: \(fromStatus)\n to: \(toStatus)\n with reason: \(reason)")
}
}
Was this example helpful?