Find places and addresses

This topic describes how to build an iOS application to find places and addresses and show them on a map. It's assumed that you've already added a map to your application as described in adding a map to your app.

The API provides a locator (class name AGSLocator) that allows you to match addresses and place names to locations on a map (also known as geocoding), and conversely, match locations on a map to real world addresses (also known as reverse geocoding).

Learn more about the locator

The locator relies on Esri's geocode web services, which can be hosted in Esri's cloud platform (ArcGIS Online) or on your on-premise servers. In this topic, you'll use Esri's World geocode web service on ArcGIS Online.

1. Allow a user to input an address or place name

First, you'll modify the user interface (UI) to allow a user to input the name of a place or an address. To do this, you'll add a search bar just above the map.

UI in interface builder

When adding UI components, you typically also need to specify Auto Layout constraints for the components so that they can be properly sized and positioned when the layout of your application changes, for example, when the device is rotated. For the sake of simplicity, we will skip setting layout constraints in this tutorial, but you can look at the accompanying tutorial app to see these constraints in action.

Next, set your view controller as the search bar's delegate by connecting the search bar's delegate connector to the file owner. This allows the search bar to notify the view controller when the user taps the Search button on the on-screen keypad.

Wiring up delegate in interface builder

2. Find matching locations

When a user taps the Search button, the delegate's searchBarSearchButtonClicked: method is invoked. You need to implement this method in your view controller's implementation file (.m). In this method, you first instantiate the locator if you haven't already done so. You also need to set the locator's delegate. The locator informs its delegate when operations complete successfully or when errors are encountered. To get results from the locator and to properly handle any errors, you must set one of your classes as the locator’s delegate. In this example, you'll set the view controller as the locator's delegate by making the view controller adopt the AGSLocatorDelegate protocol and setting the locator's delegate property to self.

As part of adopting the protocol, you need to add the declaration to the view controller's header file (.h), and implement the methods defined in the protocol in the view controller's implementation file (.m).

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
    //Hide the keyboard
    [searchBar resignFirstResponder];
         //Add a graphics layer to the map. This layer will hold geocoding results
        self.graphicsLayer = [AGSGraphicsLayer graphicsLayer];
        [self.mapView addMapLayer:self.graphicsLayer withName:@"Results"];

        //Assign a simple renderer to the layer to display results as pushpins 
        AGSPictureMarkerSymbol* pushpin = [AGSPictureMarkerSymbol pictureMarkerSymbolWithImageNamed:@"BluePushpin.png"];
        pushpin.offset = CGPointMake(9,16);
        pushpin.leaderPoint = CGPointMake(-9,11);
        AGSSimpleRenderer* renderer = [AGSSimpleRenderer simpleRendererWithSymbol:pushpin];
        self.graphicsLayer.renderer = renderer;
        //Clear out previous results if we already have a graphics layer
        [self.graphicsLayer removeAllGraphics];
        //Create the AGSLocator pointing to the geocode service on ArcGIS Online
        //Set the delegate so that we are informed through AGSLocatorDelegate methods
        self.locator = [AGSLocator locatorWithURL:[NSURL URLWithString:@""]];
        self.locator.delegate = self;  
    //Set the parameters
    AGSLocatorFindParameters* params = [[AGSLocatorFindParameters alloc]init];
    params.text = searchBar.text;
    params.outFields = @[@"*"];
    params.outSpatialReference = self.mapView.spatialReference;
    //Kick off the geocoding operation.
    //This will invoke the geocode service on a background thread.
    [self.locator findWithParameters:params];

3. Display locations on the map

In this section, you'll add the results returned by the locator to the map. For each result, you'll add a graphic to the graphics layer. Recall that in the previous section you assigned a renderer to the graphics layer that will draw each graphic as a pushpin.

When the locator completes its operation, it will inform the view controller by invoking the locator:operation:didFind: method defined in the AGSLocatorDelegate protocol. In this method, you'll first check whether the results array is empty, and if so, inform the user that no results were found. If results were found, you'll iterate through them and add the graphic corresponding to each result to the graphics layer. Finally, you'll zoom in on the map to the results.

- (void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFind:(NSArray *)results {
    if (results == nil || [results count] == 0)
        //show alert if we didn't get results
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Results"
                                                        message:@"No Results Found"
        [alert show];
        //Add a graphic for each result
        for (AGSLocatorFindResult* result in results) {
            AGSGraphic* graphic = result.graphic;
            [self.graphicsLayer addGraphic:graphic];
        //Zoom in to the results
        AGSMutableEnvelope *extent = [self.graphicsLayer.fullEnvelope mutableCopy];
        [extent expandByFactor:1.5];
        [self.mapView zoomToEnvelope:extent animated:YES];

4. Display information about the locations

In this section, you'll allow a user to tap a graphic to get information about each location. You'll display the name or address of the location as well as its latitude and longitude coordinates.

Each graphic contains a list of attributes that hold information about that location. To display this information in a callout, you'll instantiate a callout template and set its title and detail properties to be the name of an attribute (enclosed within ${} tokens) of your choice. In this example, you'll set the title to ${Match_addr}. The final thing to do is to assign this callout template as the layer's calloutDelegate property. Now, whenever a user taps on any graphic in this layer, a callout is automatically displayed and the callout's title automatically contains the value of the Match_addr attribute of the graphic.

- (void)locator:(AGSLocator *)locator operation:(NSOperation *)op didFind:(NSArray *)results {
    if (results == nil || [results count] == 0)
        //Create a callout template if we haven't done so already
            self.calloutTemplate = [[AGSCalloutTemplate alloc]init];
            self.calloutTemplate.titleTemplate = @"${Match_addr}";
            self.calloutTemplate.detailTemplate = [NSString stringWithFormat:@"${DisplayY}%@ ${DisplayX}%@", @"\u00b0", @"\u00b0"];  
            //Assign the callout template to the layer
            self.graphicsLayer.calloutDelegate = self.calloutTemplate;
        for (AGSLocatorFindResult* result in results) {
            AGSGraphic* graphic = result.graphic;
            [self.graphicsLayer addGraphic:graphic];

The callout also contains an accessory button you can use to bring up a dialog box or a secondary view to show more information. This functionality is not covered in this topic, but you can refer to the documentation for more information on how to do so.

5. Run the application

At this point, you've written all the code required to allow a user to enter an address or place name and display its location on a map. The user can also tap the map to get details about the location. Now you need to declare the properties you've been using and the delegate protocols you've adopted.

@interface ViewController : UIViewController < AGSMapViewLayerDelegate, UISearchBarDelegate, AGSLocatorDelegate, AGSCalloutDelegate> {

@property (nonatomic, strong) IBOutlet AGSMapView *mapView;
@property (nonatomic, strong) AGSGraphicsLayer *graphicsLayer;
@property (nonatomic, strong) AGSLocator *locator;
@property (nonatomic, strong) AGSCalloutTemplate *calloutTemplate;