Show device location

View on GitHub

Show your current position on the map, as well as switch between different types of auto-pan modes.

Screenshot of show device location sample

Use case

When using a map within a GIS application, it may be helpful for a user to know their own location within a map, whether that's to aid the user's navigation or to provide an easy mean of identifying/collecting geospatial information at their location.

How to use the sample

Tap the "Location Settings" button to open the settings interface.

Toggle "Show Location" to change the visibility of the location indicator in the map view. It will be asked by the system to provide permission to use the device's location, if the user have not yet used location services in this app.

Change the "Auto-Pan Mode" to choose if and how the SDK will position the map view's viewpoint to keep the location indicator in-frame. A menu will appear with the following options to change the LocationDisplay.AutoPanMode:

  • Off: Starts the location display with no auto-pan mode set.
  • Re-Center: Starts the location display with auto-pan mode set to recenter.
  • Navigation: Starts the location display with auto-pan mode set to navigation.
  • Compass Navigation: Starts the location display with auto-pan mode set to compassNavigation.

How it works

  1. Create a LocationDisplay object with a location data source.
  2. Use the locationDisplay(_:) map view modifier to set the location display for the map view.
  3. Use the LocationDisplay.AutoPanMode property to change how the map behaves when location updates are received.
  4. Use the start() and stop() methods on the location display's data source as necessary.

Relevant API

  • LocationDataSource
  • LocationDisplay
  • LocationDisplay.AutoPanMode
  • MapView
  • MapView.locationDisplay(_:)

Additional information

Location permissions are required for this sample.

Note: The default location data source, SystemLocationDataSource, needs the app to be authorized in order to access the device's location. The app must contain appropriate purpose strings (NSLocationWhenInUseUsageDescription, or NSLocationAlwaysAndWhenInUseUsageDescription keys) along with a brief description of how you use location services in the project's Info tab.

Please read the documentation below for further details.

Tags

compass, GPS, location, map, mobile, navigation

Sample Code

ShowDeviceLocationView.swift
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2022 Esri
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import ArcGIS
import CoreLocation
import SwiftUI

struct ShowDeviceLocationView: View {
    /// The error shown in the error alert.
    @State private var error: Error?

    /// A Boolean value indicating whether the settings button is disabled.
    @State private var settingsButtonIsDisabled = true

    /// The view model for this sample.
    @StateObject private var model = Model()

    var body: some View {
        MapView(map: model.map)
            .locationDisplay(model.locationDisplay)
            .task {
                guard model.locationDisplay.dataSource.status != .started else {
                    return
                }
                do {
                    try await model.startLocationDataSource()
                    settingsButtonIsDisabled = false
                    // Updates the current auto-pan mode if it does not match the
                    // location display's auto-pan mode.
                    for await mode in model.locationDisplay.$autoPanMode {
                        if model.autoPanMode != mode {
                            model.autoPanMode = mode
                        }
                    }
                } catch {
                    // Shows an alert with an error if starting the data source fails.
                    self.error = error
                }
            }
            .onDisappear {
                model.stopLocationDataSource()
            }
            .toolbar {
                ToolbarItem(placement: .bottomBar) {
                    Menu("Location Settings") {
                        Toggle("Show Location", isOn: $model.isShowingLocation)

                        Picker("Auto-Pan Mode", selection: $model.autoPanMode) {
                            ForEach(LocationDisplay.AutoPanMode.allCases, id: \.self) { mode in
                                Label(mode.label, image: mode.imageName)
                                    .imageScale(.large)
                            }
                        }
                    }
                    .disabled(settingsButtonIsDisabled)
                }
            }
            .errorAlert(presentingError: $error)
    }
}

private extension ShowDeviceLocationView {
    /// The model used to store the geo model and other expensive objects
    /// used in this view.
    @MainActor
    class Model: ObservableObject {
        /// A Boolean value indicating whether to show the device location.
        @Published var isShowingLocation: Bool {
            didSet {
                locationDisplay.showsLocation = isShowingLocation
            }
        }

        /// The current auto-pan mode.
        @Published var autoPanMode: LocationDisplay.AutoPanMode {
            didSet {
                locationDisplay.autoPanMode = autoPanMode
            }
        }

        /// A map with a standard imagery basemap style.
        let map = Map(basemapStyle: .arcGISImageryStandard)

        /// A location display using the system location data source.
        let locationDisplay: LocationDisplay

        init() {
            let locationDisplay = LocationDisplay(dataSource: SystemLocationDataSource())
            self.locationDisplay = locationDisplay
            self.isShowingLocation = locationDisplay.showsLocation
            self.autoPanMode = locationDisplay.autoPanMode
        }

        /// Starts the location data source.
        func startLocationDataSource() async throws {
            // Requests location permission if it has not yet been determined.
            let locationManager = CLLocationManager()
            if locationManager.authorizationStatus == .notDetermined {
                locationManager.requestWhenInUseAuthorization()
            }
            // Starts the location display data source.
            try await locationDisplay.dataSource.start()
        }

        /// Stops the location data source.
        func stopLocationDataSource() {
            Task {
                await locationDisplay.dataSource.stop()
            }
        }
    }
}

private extension LocationDisplay.AutoPanMode {
    /// A human-readable label for each auto-pan mode.
    var label: String {
        switch self {
        case .off: return "Auto-Pan Off"
        case .recenter: return "Recenter"
        case .navigation: return "Navigation"
        case .compassNavigation: return "Compass Navigation"
        @unknown default: return "Unknown"
        }
    }

    /// The image name for each auto-pan mode.
    var imageName: String {
        switch self {
        case .off: return "LocationDisplayOffIcon"
        case .recenter: return "LocationDisplayDefaultIcon"
        case .navigation: return "LocationDisplayNavigationIcon"
        case .compassNavigation: return "LocationDisplayHeadingIcon"
        @unknown default: return "LocationDisplayOffIcon"
        }
    }
}

#Preview {
    NavigationStack {
        ShowDeviceLocationView()
    }
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.