Display device location with autopan modes

View on GitHub
Sample viewer app

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

Image of display device location 1 Image of display device location 2

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 means 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 AGSLocationDisplayAutoPanMode:

  • 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 an AGSMapView.
  2. Update the AGSLocationDisplay property of the map view.
  3. Use the autoPanMode property to change the how the map behaves when location updates are received.
  4. Use start() and stop() on the AGSLocationDisplay object as necessary.

Relevant API

  • AGSLocationDisplay
  • AGSLocationDisplayAutoPanMode
  • AGSMap
  • AGSMapView

Additional information

Location permissions are required for this sample.

Note: As of iOS 8, you are required to request the user's permission to enable location services. You must include either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription along with a brief description of how you use location services in the Info plist of your project.

Tags

compass, GPS, location, map, mobile, navigation

Sample Code

DisplayLocationSettingsViewController.swiftDisplayLocationViewController.swift
                                                                                                                                              
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
// Copyright 2018 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
//
//   http://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 UIKit
import ArcGIS

class DisplayLocationSettingsViewController: UITableViewController {
    // MARK: - Outlets

    @IBOutlet weak var showSwitch: UISwitch?
    @IBOutlet weak var autoPanModeCell: UITableViewCell?

    // MARK: - Model

    /// The SDK object for displaying the device location. Attached to the map view.
    weak var locationDisplay: AGSLocationDisplay? {
        didSet {
            updateUIForLocationDisplay()
        }
    }

    /// Returns a suitable interface label `String` for the mode.
    private func label(for autoPanMode: AGSLocationDisplayAutoPanMode) -> String {
        switch autoPanMode {
        case .off:
            return "Off"
        case .recenter:
            return "Re-Center"
        case .navigation:
            return "Navigation"
        case .compassNavigation:
            return "Compass Navigation"
        @unknown default:
            return "Unknown"
        }
    }

    private func icon(for autoPanMode: AGSLocationDisplayAutoPanMode) -> UIImage? {
        switch autoPanMode {
        case .off:
            return #imageLiteral(resourceName: "LocationDisplayOffIcon")
        case .recenter:
            return #imageLiteral(resourceName: "LocationDisplayDefaultIcon")
        case .navigation:
            return #imageLiteral(resourceName: "LocationDisplayNavigationIcon")
        case .compassNavigation:
            return #imageLiteral(resourceName: "LocationDisplayHeadingIcon")
        @unknown default:
            return nil
        }
    }

    // MARK: - Views

    override func viewDidLoad() {
        super.viewDidLoad()

        // setup the UI for the initial model values
        updateUIForLocationDisplay()

        // The auto-pan mode may be updated by the SDK as well as the user,
        // so use the change handler to set the UI
        locationDisplay?.autoPanModeChangedHandler = { [weak self] (_) in
            // update the UI for the new mode
            self?.updateUIForAutoPanMode()
        }
    }

    private func updateUIForLocationDisplay() {
        showSwitch?.isOn = locationDisplay?.started == true
        updateUIForAutoPanMode()
    }

    private func updateUIForAutoPanMode() {
        if let autoPanMode = locationDisplay?.autoPanMode {
            autoPanModeCell?.detailTextLabel?.text = label(for: autoPanMode)
        }
    }

    // MARK: - Actions

    @IBAction func showLocationSwitchAction(_ sender: UISwitch) {
        guard let locationDisplay = locationDisplay,
            // don't restart showing the location if it's already started
            locationDisplay.started != sender.isOn else {
                return
        }

        if sender.isOn {
            // To be able to request user permissions to get the device location,
            // make sure to add the location request field in the info.plist file

            // attempt to start showing the device location
            locationDisplay.start { [weak self] (error: Error?) in
                if let error = error {
                    // show the error if one occurred
                    self?.presentAlert(error: error)
                }
            }
        } else {
            // stop showing the device location
            locationDisplay.stop()
        }
    }

    // MARK: - UITableViewDelegate

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        guard let cell = tableView.cellForRow(at: indexPath) else {
            return
        }

        /// The modes in the order we want to display them in the interface.
        let orderedAutoPanModes = [AGSLocationDisplayAutoPanMode.off, .recenter, .navigation, .compassNavigation]

        if cell == autoPanModeCell,
            let autoPanMode = locationDisplay?.autoPanMode,
            let selectedIndex = orderedAutoPanModes.firstIndex(of: autoPanMode) {
            let options = orderedAutoPanModes.map { OptionsTableViewController.Option(label: label(for: $0), image: icon(for: $0)) }

            let controller = OptionsTableViewController(options: options, selectedIndex: selectedIndex) { (index) in
                // get the mode for the index
                let autoPanMode = orderedAutoPanModes[index]
                // set the displayed location mode to the selected one
                self.locationDisplay?.autoPanMode = autoPanMode
            }
            controller.title = cell.textLabel?.text
            show(controller, sender: self)
        }
    }
}

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