Pinch to change field of view
This example demonstrates how to repurpose a pinch gesture to control the camera's vertical field of view (FOV) instead of the default pinch-to-zoom behavior, using the Mapbox Maps SDK for iOS. The PinchToChangeFovExample class disables the built-in pinch gesture through GestureOptions/pinchEnabled and attaches its own UIPinchGestureRecognizer so the gesture is used only to change verticalFov.
In the viewDidLoad method, the map is initialized with a high pitch so that changes to the field of view are more noticeable, and the example sets mapView.gestures.options.pinchEnabled to false before adding the custom UIPinchGestureRecognizer. The handlePinch(_:) method captures the current field of view from CameraState/verticalFov when the gesture begins, then as the gesture changes, divides that starting value by the gesture's scale to compute a new field of view, clamping the result between 1° and 90°. The new value is applied with MapboxMap.setCamera(to:) and reflected in a label overlaid on the map, so pinching out narrows the field of view while pinching in widens it.
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.
import UIKit
@_spi(Experimental) import MapboxMaps
final class ViewController: UIViewController {
private var mapView: MapView!
private var fovLabel: UILabel!
private var fovAtGestureStart: CGFloat = 36.87
private let fovRange: ClosedRange<CGFloat> = 1...90
override func viewDidLoad() {
super.viewDidLoad()
let camera = CameraOptions(
center: CLLocationCoordinate2D(latitude: 59.3419, longitude: 18.0669),
zoom: 16.5,
bearing: 30,
pitch: 70)
let options = MapInitOptions(cameraOptions: camera)
mapView = MapView(frame: view.bounds, mapInitOptions: options)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(mapView)
// Disable built-in pinch-to-zoom so we can repurpose the pinch gesture for FOV
mapView.gestures.options.pinchEnabled = false
let pinch = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch(_:)))
mapView.addGestureRecognizer(pinch)
setupLabel()
updateLabel(mapView.mapboxMap.cameraState.verticalFov)
}
private func setupLabel() {
fovLabel = UILabel()
fovLabel.textColor = .white
fovLabel.textAlignment = .center
fovLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
fovLabel.layer.cornerRadius = 10
fovLabel.layer.masksToBounds = true
fovLabel.font = .monospacedSystemFont(ofSize: 15, weight: .medium)
fovLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(fovLabel)
let safe = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
fovLabel.bottomAnchor.constraint(equalTo: safe.bottomAnchor, constant: -20),
fovLabel.centerXAnchor.constraint(equalTo: safe.centerXAnchor),
fovLabel.widthAnchor.constraint(equalToConstant: 200),
fovLabel.heightAnchor.constraint(equalToConstant: 44)
])
}
private func updateLabel(_ fov: CGFloat) {
fovLabel.text = String(format: "FOV: %.1f°", fov)
}
@objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
switch gesture.state {
case .began:
fovAtGestureStart = mapView.mapboxMap.cameraState.verticalFov
case .changed:
guard gesture.scale > 0 else { return }
// Pinch out (scale > 1) narrows FOV (like zooming in); pinch in widens it
let newFov = min(fovRange.upperBound, max(fovRange.lowerBound, fovAtGestureStart / gesture.scale))
mapView.mapboxMap.setCamera(to: CameraOptions(verticalFov: newFov))
updateLabel(newFov)
default:
break
}
}
}