メインコンテンツまでスキップ

Advanced viewport gestures

This example configures the viewport behavior in an app using the Mapbox Maps SDK for iOS. It allows the viewport to switch between a follow state and an overview state triggered by a single tap on the map. In the "follow puck" state, zoom and pitch gestures are enabled alongside automatic updates from the viewport state. For a comprehensive user experience, it is recommended to simulate the example with the "Features > Location > Freeway Drive" setting in the iOS simulator.

The implementation involves managing the different states follow and overview utilizing FollowPuckViewportState and OverviewViewportState to control the viewport behavior. Additionally, the example demonstrates customization of gesture interactions based on the viewport state, such as enabling or disabling pan and pinch gestures. The interactions trigger transitions in the viewport, providing a smooth user experience through seamless state changes. The example also includes gesture management through the GestureManagerDelegate extension and monitoring viewport status changes via the ViewportStatusObserver protocol.

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.

AdvancedViewportGesturesExample.swift
import UIKit
import MapboxMaps

// This example configures the viewport so that when it's in the follow puck state,
// zoom and pitch gestures work alongside updates coming from the state itself.
// Single tap on the map to switch to the overview state.
//
// 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 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 = .course
mapView.location.options.puckBearingEnabled = true

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

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

mapView.gestures.delegate = self

syncWithState()
}



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

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)
case .overview:
mapView.viewport.transition(to: overviewViewportState)
}

mapView.viewport.options.transitionsToIdleUponUserInteraction = state == .overview
mapView.gestures.options.panEnabled = state == .overview
mapView.gestures.options.pinchEnabled = state == .overview
}
}

extension ViewController: GestureManagerDelegate {
func gestureManager(_ gestureManager: GestureManager, didBegin gestureType: GestureType) {
switch gestureType {
case .pitch:
if state == .following {
followPuckViewportState.options.pitch = nil
}
case .doubleTapToZoomIn, .doubleTouchToZoomOut, .quickZoom:
if state == .following {
followPuckViewportState.options.zoom = nil
}
default:
break
}
}

func gestureManager(_ gestureManager: GestureManager, didEnd gestureType: GestureType, willAnimate: Bool) {
switch gestureType {
case .pitch:
if state == .following {
followPuckViewportState.options.pitch = mapView.mapboxMap.cameraState.pitch
}
case .quickZoom:
if state == .following {
followPuckViewportState.options.zoom = mapView.mapboxMap.cameraState.zoom
}
case .singleTap:
toggleViewportState()
default:
break
}
}

func gestureManager(_ gestureManager: GestureManager, didEndAnimatingFor gestureType: GestureType) {
switch gestureType {
case .doubleTapToZoomIn, .doubleTouchToZoomOut:
if state == .following {
followPuckViewportState.options.zoom = mapView.mapboxMap.cameraState.zoom
}
default:
break
}
}
}

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)")
}
}
このexampleは役に立ちましたか?