Search for an address

Prerequisites
The following are required for this tutorial:
- An ArcGIS account to access your API keys. If you don't have an account, sign up for free.
- Your system meets the system requirements.
- The ArcGIS Runtime API for iOS is installed.
Steps
Open a Xcode project
To start the tutorial, complete the Display a map tutorial or download and unzip the solution.
Open the
.xcodeproj
file in Xcode.If you downloaded the solution project, set your API key.
An API Key enables access to services, web maps, and web scenes hosted in ArcGIS Online.
Go to your developer dashboard to get your API key. For these tutorials, use your default API key. It is scoped to include all of the services demonstrated in the tutorials.
In Xcode, in the Project Navigator, click AppDelegate.swift.
In the editor, set the
apiKey
property on theAGSArcGISRuntimeEnvironment
with your API key.AppDelegate.swiftChange line // Copyright 2020 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 UIKit import ArcGIS @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Note: it is not best practice to store API keys in source code. // The API key is referenced here for the convenience of this tutorial. AGSArcGISRuntimeEnvironment.apiKey = "YOUR_API_KEY" return true } }
Add a search bar to the UI
To search an address using the application, add a UI element to prompt the user for text input.
In Xcode, in the Project Navigator, click ViewController.swift.
In the editor, extend
ViewController
to conform to theUISearchBarDelegate
protocol.The
UISearchBarDelegate
will allowViewController
to receive user interaction messages from aUISearchBar
.ViewController.swiftAdd line. Add line. Add line. 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 146 145 144 143 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 142 141 140 139 138 137 136 135 134 133 132 132 132 132 131 130 129 128 127 126 125 124 123 122 121 120 119 118 117 116 115 114 113 112 111 110 109 108 107 106 106 106 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 86 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 50 50 51 52 52 52 52 52 52 52 52 52 52 52 52 52 52 53// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Define a private method named
setupSearchBar()
. InsetupSearchBar()
create anUISearchBar
with these initial values and assign it tonavigationItem.titleView
.ViewController.swiftAdd line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 45 44 43 42 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 42 43 44 45 46 47 48 49 50 50 50 50 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 24 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 4 4 3 2 1 0 -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 -32 -32 -32 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -45// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
In Xcode, in the Project Navigator, click Main.storyboard.
In the editor, select
ViewController
. In the menu bar, click Editor > Embed In > Navigation Controller.Embedding
ViewController
within a Navigation Controller will place a navigation bar at the top ofViewController
. Inside the navigation bar you will find the search bar.In Xcode, in the Project Navigator, click ViewController.swift.
In the editor, in the
viewDidLoad()
method, callsetupSearchBar()
.ViewController.swiftAdd line. 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 21 22 23 24 25 26 26 26 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -19 -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 -55 -55 -55 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -68// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Add graphics to the map view
A graphics overlay is a container for graphics. Graphics are added as a visual means to display the search result on the map.
Create a private
AGSGraphic
property namedtextGraphic
. This graphic will be used to display the result's text label.An
AGSTextSymbol
is used to display text at a location on the map view.ViewController.swiftAdd line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 63 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 62 61 60 59 58 57 56 57 58 59 60 61 62 63 64 65 66 66 65 64 63 62 61 60 59 58 57 57 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 37 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1 1 1 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -12// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Create a private
AGSGraphic
property namedmarkerGraphic
. This graphic will be used to display the result's location.An
AGSSimpleMarkerSymbol
is used to display a location on the map view.ViewController.swiftAdd line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 74 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 73 72 71 70 69 68 67 67 67 67 67 67 67 67 67 67 67 67 68 69 70 71 72 73 74 75 75 75 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 55 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 19 19 19 19 18 17 16 15 14 13 12 11 10 9 8 7 6 6// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Define a private method named
setupGraphics()
. InsetupGraphics()
create anAGSGraphicsOverlay
. AppendtextGraphic
andmarkerGraphic
to the graphics overlay then add the graphics overlay to the map view.ViewController.swiftAdd line. Add line. Add line. Add line. Add line. 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 57 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 57 58 59 60 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 41 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 5 5 5 5 4 3 2 1 0 -1 -2 -3 -4 -5 -6 -7 -8 -8// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Because
textGraphic
andmarkerGraphic
haven't yet specified a geometry, they will not be visible.In the
viewDidLoad()
method, callsetupGraphics()
.ViewController.swiftAdd line. 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 21 22 23 24 25 26 27 28 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 9 9 8 7 6 5 4 3 2 1 0 -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 -27 -27 -27 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -40// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Define app geocode results status
The app will leverage a state-machine design pattern to ensure the contents of the map reflect the state of the search result. This design pattern supports wrangling multiple asynchronous requests to the world geocoding service into a single state variable result, ensuring the reliability of search results.
Define an enum named
ResultStatus
with two cases. The first case,none
, is used when there is no result or an error is returned. The second case,result
, is used when a result is found.ViewController.swiftAdd line. Add line. Add line. Add line. 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 87 88 89 90 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 75 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 39 39 39 39 38 37 36 35 34 33 32 31 30 29 28 27 26 26// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Create a
ResultStatus
property namedstatus
. Setting this property will updatetextGraphic
andmarkerGraphic
with values from the result.ViewController.swiftAdd line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. Add line. 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 105 105 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 69 69 69 69 68 67 66 65 64 63 62 61 60 59 58 57 56 56// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Create a locator task with geocode parameters
Geocoding is implemented with a locator, typically created by referencing a service such as the Geocoding service or, for offline geocoding, by referencing locator data contained in a mobile package. Geocoding parameters can be used to fine-tune the results, such as setting the maximum number of results or requesting additional attributes in the results.
Create a private
AGSLocatorTask
property namedlocator
with the Geocoding service URL.A locator task is used to convert an address to a point (geocode) or vice-versa (reverse geocode). An address includes any type of information that distinguishes a place. A locator involves finding matching locations for a given address. Reverse-geocoding is the opposite and finds the closest address for a given point.
ViewController.swiftAdd line. Add line. Add line. 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 109 110 111 111 110 109 108 107 106 105 104 103 102 101 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 79 79 79 79 78 77 76 75 74 73 72 71 70 69 68 67 66 66// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Create a private optional
AGSCancelable
property namedcurrentGeocodeOperation
that will maintain a reference to the geocode operation. If the user submits a second query before the current one completes, it can be used to cancel the operation.ViewController.swiftAdd line. 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 109 110 111 112 113 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 114 113 113 113 113 113 112 111 110 109 108 107 106 105 104 103 102 101 100 100// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Define a private method named
geocode(with searchText: String)
and cancel the current geocode operation, if one exists.ViewController.swiftAdd line. Add line. Add line. Add line. 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 108 109 110 111 112 113 114 115 116 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 117 118 118 118 118 118 118 117 116 115 114 113 112 111 110 109 108 107 106 105 105// Copyright 2020 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 UIKit import ArcGIS class ViewController: UIViewController { @IBOutlet weak var mapView: AGSMapView! override func viewDidLoad() { super.viewDidLoad() setupMap() setupSearchBar() setupGraphics() } // MARK: - Map private func setupMap() { mapView.map = AGSMap(basemapStyle: .arcGISTopographic) mapView.setViewpoint( AGSViewpoint( latitude: 34.02700, longitude: -118.80543, scale: 144447.638572 ) ) } // MARK: - Search Bar private func setupSearchBar() { navigationItem.titleView = { let searchBar = UISearchBar() searchBar.delegate = self searchBar.showsCancelButton = true searchBar.placeholder = "Search for an address" return searchBar }() } // MARK: - Graphics private func setupGraphics() { let graphicsOverlay = AGSGraphicsOverlay() graphicsOverlay.graphics.addObjects(from: [textGraphic, markerGraphic]) mapView.graphicsOverlays.add(graphicsOverlay) } private let textGraphic: AGSGraphic = { let textSymbol = AGSTextSymbol( text: "", color: .black, size: 14, horizontalAlignment: .center, verticalAlignment: .bottom ) return AGSGraphic(geometry: nil, symbol: textSymbol) }() private let markerGraphic: AGSGraphic = { let markerSymbol = AGSSimpleMarkerSymbol( style: .square, color: .red, size: 12 ) return AGSGraphic(geometry: nil, symbol: markerSymbol) }() // MARK: - Result enum ResultStatus { case none case result(String, AGSPoint) } var status: ResultStatus = .none { didSet { switch status { case .none: (textGraphic.symbol as! AGSTextSymbol).text = "" textGraphic.geometry = nil markerGraphic.geometry = nil case .result(let title, let location): (textGraphic.symbol as! AGSTextSymbol).text = title textGraphic.geometry = location markerGraphic.geometry = location } } } // MARK: - Geocoding private let locator = AGSLocatorTask( url: URL(string: "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer")! ) private var currentGeocodeOperation: AGSCancelable? private func geocode(with searchText: String) { currentGeocodeOperation?.cancel() let parameters: AGSGeocodeParameters = { let parameters = AGSGeocodeParameters() parameters.resultAttributeNames = ["*"] parameters.maxResults = 1 parameters.outputSpatialReference = mapView.spatialReference return parameters }() currentGeocodeOperation = self.locator.geocode(withSearchText: searchText, parameters: parameters) { [weak self] (results, error) in guard let self = self else { return } if let error = error { self.status = .none print(error.localizedDescription) return } else if let firstResult = results?.first, let extent = firstResult.extent, let location = firstResult.displayLocation { self.status = .result(firstResult.label, location) self.mapView.setViewpointGeometry(extent) } else { self.status = .none print("No results found for \(searchText).") } } } } extension ViewController: UISearchBarDelegate { func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() guard let searchText = searchBar.text, !searchText.isEmpty else { print("Nothing to search!") return } geocode(with: searchText) } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { searchBar.resignFirstResponder() } }
Create new
AGSGeocodeParameters
, and assign it to thegeocodeParameters
property. Specify the geocode's attributes as follows:Specify which attributes to return with
resultAttributeNames
.*
is used to return all attributes.Set the maximum number of results to be returned with
maxResults
.In this tutorial, only return the best match by passing in 1
. Results are ordered byscore
, so just returning the first result will return the highest scoring result.Set the spatial reference with
outputSpatialReference
.By default the output spatial reference is determined by the geocode service. For optimal performance when displaying the geocode result, ensure the returned coordinates match those of the map view by providing mapView.spatialReference
as a parameter.When geocoding an address, you can optionally provide
AGSGeocodeParameters
to control certain aspects of the geocoding operation, and specify the kinds of results to return from the locator task.
ViewController.swiftAdd line. Add line. Add line. Add line. Add line. Add line. Add line. 114 114 114 114 114