User Location
This example demonstrates how to show uer location on the map and request precise location temporarily. You will learn the following:
- How to overlay button on top of the
MapView
and how to coordinate view state between them. - How to setup the app so that it has access to location.
- How to configure the
MapView
so that the user location is shown on the map. - How to temporarily request precise location if user opted out in approximate location.
View complete source code on GitHub
More information about iOS location management: https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_standard_location_service
More information about iOS location manager: https://developer.apple.com/documentation/corelocation/cllocationmanager
Mobile App - Setup the MapView
In order to enable the alert that requests temporary precise location, please add the following into info.plist:
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
<key>MGLAccuracyAuthorizationDescription</key>
<string>Examples requires your precise location to show your location.</string>
</dict>
Request location
-
Next, you need to request user location in the
MapView
mapView.showsUserLocation = true
Request Precise Location Temporarily
-
Our example uses SwiftUI. We conditionally add a button for requesting precise location on top of the map view when the current accuracy settings is not precise. So we have two views:
LocationAwareMapView
which is wrappingMGLMapView
and makes it possible to use it in SwiftUI. In this view we will useMapView
methods to request precise location and theMGLMapViewDelegate
to respond to event which is raised when location accuracy has been changed.- Second view will contain button used to request precise location when tapped.
We need to coordinate UI state - when the precise location is requested and user accepts it, the
MGLMapViewDelegate
will raisemapViewDidFinishLoadingMap
event and we hide the button. To coordinate the state we use class derived fromObservableObject
which contain just a flag indicating if the button should be visible or not.class SharedState: ObservableObject { init(showPreciseButton: Bool) { self.showPreciseButton = showPreciseButton } @Published var showPreciseButton: Bool }
-
The method which handles
MGLMapViewDelegate
event raised when accuracy settings changed looks as follows:func mapViewDidFinishLoadingMap(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) { guard let accuracySetting = manager.accuracyAuthorization?() else { return } if accuracySetting == .reducedAccuracy { sharedState.showPreciseButton = true; } else { sharedState.showPreciseButton = false; } }
-
And the method for requesting precise location :
func requestTemporaryAuth() { guard let mapView = self.mapView else { return } let purposeKey = "MGLAccuracyAuthorizationDescription" mapView.locationManager.requestTemporaryFullAccuracyAuthorization!(withPurposeKey: purposeKey) }
-
The full code of the UI:
class LocationAwareMapView: UIViewRepresentable { @ObservedObject var sharedState: SharedState var mapView: MGLMapView? init(_ sharedState: SharedState) { self.sharedState = sharedState } func makeUIView(context: Context) -> MGLMapView { // read the key from property list let mapTilerKey = Helper.getMapTilerkey() Helper.validateKey(mapTilerKey) // Build the style url let styleURL = URL(string: "https://api.maptiler.com/maps/streets-v2/style.json?key=\(mapTilerKey)") // create the mapview let mapView = MGLMapView(frame: .zero, styleURL: styleURL) mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] mapView.logoView.isHidden = true mapView.showsUserLocation = true // set the map view delegate so that we can respond to events mapView.delegate = context.coordinator // keep map view reference, it will be needed when requesting permissions self.mapView = mapView; return mapView } func updateUIView(_ uiView: MGLMapView, context: Context) {} func makeCoordinator() -> LocationAwareMapView.Coordinator { Coordinator(self, sharedState) } final class Coordinator: NSObject, MGLMapViewDelegate { var control: LocationAwareMapView @ObservedObject var sharedState: SharedState init(_ control: LocationAwareMapView, _ sharedState: SharedState) { self.control = control self.sharedState = sharedState } } @available(iOS 14, *) func mapViewDidFinishLoadingMap(_ mapView: MGLMapView, didChangeLocationManagerAuthorization manager: MGLLocationManager) { guard let accuracySetting = manager.accuracyAuthorization?() else { return } if accuracySetting == .reducedAccuracy { sharedState.showPreciseButton = true; } else { sharedState.showPreciseButton = false; } } @available(iOS 14, *) @objc public func requestTemporaryAuth() { guard let mapView = self.mapView else { return } let purposeKey = "MGLAccuracyAuthorizationDescription" mapView.locationManager.requestTemporaryFullAccuracyAuthorization!(withPurposeKey: purposeKey) } }
-
The full code of the wrapped
MapView
:struct LocationPermissions: View { @ObservedObject var sharedState: SharedState; var wrappedMapView: LocationAwareMapView; init() { let sharedState = SharedState(showPreciseButton: true) self.wrappedMapView = LocationAwareMapView(sharedState) self.sharedState = sharedState } var body: some View { ZStack { wrappedMapView.navigationTitle("Location") if sharedState.showPreciseButton { VStack { Button( action: {wrappedMapView.requestTemporaryAuth()}, label: {Text("Turn Precise On")}) .background(Color.gray) } } } } }
iOS SDK
Examples
SDK JS Reference
- Annotations
- Appendices
- NSExpression(MGLAdditions)
- NSValue(MGLAdditions)
- NSValue(MGLCircleStyleLayerAdditions)
- NSValue(MGLFillExtrusionStyleLayerAdditions)
- NSValue(MGLFillStyleLayerAdditions)
- NSValue(MGLHillshadeStyleLayerAdditions)
- NSValue(MGLLineStyleLayerAdditions)
- NSValue(MGLRasterStyleLayerAdditions)
- NSValue(MGLSymbolStyleLayerAdditions)
- MGLAccountManager
- MGLAnnotationImage
- MGLAnnotationView
- MGLAttributedExpression
- MGLAttributionInfo
- MGLBackgroundStyleLayer
- MGLCircleStyleLayer
- MGLClockDirectionFormatter
- MGLCompassButton
- MGLCompassDirectionFormatter
- MGLComputedShapeSource
- MGLCoordinateFormatter
- MGLDistanceFormatter
- MGLFillExtrusionStyleLayer
- MGLFillStyleLayer
- MGLForegroundStyleLayer
- MGLHeatmapStyleLayer
- MGLHillshadeStyleLayer
- MGLImageSource
- MGLLight
- MGLLineStyleLayer
- MGLLoggingConfiguration
- MGLMapCamera
- MGLMapSnapshot
- MGLMapSnapshotOptions
- MGLMapSnapshotOverlay
- MGLMapSnapshotter
- MGLMapView
- MGLMultiPoint
- MGLMultiPolygon
- MGLMultiPolyline
- MGLNetworkConfiguration
- MGLOfflinePack
- MGLOfflineStorage
- MGLPointAnnotation
- MGLPointCollection
- MGLPolygon
- MGLPolyline
- MGLRasterStyleLayer
- MGLRasterTileSource
- MGLShape
- MGLShapeCollection
- MGLShapeOfflineRegion
- MGLShapeSource
- MGLSource
- MGLStyle
- MGLStyleLayer
- MGLSymbolStyleLayer
- MGLTilePyramidOfflineRegion
- MGLTileSource
- MGLUserLocation
- MGLUserLocationAnnotationView
- MGLUserLocationAnnotationViewStyle
- MGLVectorStyleLayer
- MGLVectorTileSource
- MGLAnnotationVerticalAlignment
- MGLAnnotationViewDragState
- MGLAttributionInfoStyle
- MGLCirclePitchAlignment
- MGLCircleScaleAlignment
- MGLCircleTranslationAnchor
- MGLDEMEncoding
- MGLErrorCode
- MGLFillExtrusionTranslationAnchor
- MGLFillTranslationAnchor
- MGLHillshadeIlluminationAnchor
- MGLIconAnchor
- MGLIconPitchAlignment
- MGLIconRotationAlignment
- MGLIconTextFit
- MGLIconTranslationAnchor
- MGLLightAnchor
- MGLLineCap
- MGLLineJoin
- MGLLineTranslationAnchor
- MGLLoggingLevel
- MGLMapDebugMaskOptions
- MGLOfflinePackState
- MGLOrnamentPosition
- MGLOrnamentVisibility
- MGLRasterResamplingMode
- MGLResourceKind
- MGLSymbolPlacement
- MGLSymbolZOrder
- MGLTextAnchor
- MGLTextJustification
- MGLTextPitchAlignment
- MGLTextRotationAlignment
- MGLTextTransform
- MGLTextTranslationAnchor
- MGLTextWritingMode
- MGLTileCoordinateSystem
- MGLUserTrackingMode
- Formatters
- Geometry
- Location Updates
- Maps
- Offline Maps
- Other Categories
- Other Classes
- Other Constants
- Other Enumerations
- Other Functions
- Other Protocols
- Other Structures
- Other Type Definitions
- Primitive Shapes
- MGLAnnotation
- MGLCalloutView
- MGLCalloutViewDelegate
- MGLCluster
- MGLComputedShapeSourceDataSource
- MGLFeature
- MGLLocationManager
- MGLLocationManagerDelegate
- MGLMapSnapshotterDelegate
- MGLMapViewDelegate
- MGLOfflineRegion
- MGLOfflineStorageDelegate
- MGLOverlay
- MGLStylable
- MGLCoordinateBounds
- MGLCoordinateQuad
- MGLCoordinateSpan
- MGLOfflinePackProgress
- MGLSphericalPosition
- MGLTransition
- Style Content
- Style Layers
- Style Primitives
- Styling the Map
- MGLCoordinateBounds
- MGLCoordinateQuad
- MGLCoordinateSpan
- MGLOfflinePackProgress
- MGLSphericalPosition
- MGLTransition
- User Interaction
- Customizing Fonts
- Information for Style Authors
- Gesture Recognizers
- Info.plist Keys
- Migrating to Expressions
- Predicates and expressions
- Tile URL Templates
- Working with GeoJSON Data