Managing Graphic features

Once you have created a Graphics layer, you need to add Graphics to it in order to display data. In most cases, you will use Graphics that have been created by actions such as executing a query or drawing a shape on the Map. To add a Graphic, you must:

The discussion that follows will show you how to create and add Graphics to a Graphics layer.

Retreiving Graphics layers from a Map View

If you want to access a Graphics layer that was previously added to a map you will need to know either the name of the Graphics layer or the index associated with that layer. There are two properties of AGSMapView which will allow you to get information about layers associated with a map: mapLayerNames and mapLayerViews.

The mapLayerNames method returns an array of the names of layers currently added to the map. The order of the layer names reflects the order of the map layers, with layer at index 0 at the bottom and the last layer name at the top.

//Get the array of map layer names
NSArray* mapLayerNamesArray = self.mapView.mapLayerNames;

You can get the layer name directly if you know the index of the layer:

//get the name of the first layer (index 0)
NSString* sLayerName = [self.mapView.mapLayerNames
objectAtIndex:0];

In order to get a Graphics layer (or any other map layer) from the map you will use the mapLayerViews method. mapLayerViews returns an NSDictionary object. This is a collection of key/value pairs. The key is a unique string which is the layer name used when adding the layer to the map (via the AGSMapView addMapLayer method). The value is the the layer view object.

This layer view object implements the AGSLayerView protocol, which will be used to get the actual Graphics layer (or any other map layer). The code below gets the dictionary of layer name/layer view pairs then gets the layer view from that dictionary using the name of the layer. Since AGSLayerView is not a class but a protocol, the syntax for declaring the local variable is a little different:

//Get the dictionary of layer views
NSDictionary<AGSLayerView> *myLayerViewDict = self.mapView.mapLayerViews;

//get the AGSLayerView for the layer named “Graphics Layer”
//Note that ‘id<AGSLayerView>’ syntax allows for the creation
//of a local variable of the correct type
id<AGSLayerView> myGraphicsLayerView = [myLayerViewDict objectForKey:@”Graphics Layer”];

Alternately, you could do it all in one step:

id<AGSLayerView> myGraphicsLayerView =
[self.mapView.mapLayerViews objectForKey:@”Graphics Layer”];

The layer name can either be a literal string, as in the above code, or one retrieved from the array returned by the mapLayerNames method.

The AGSLayerView protocol has properties for the name of the layer, the map view the layer is in, the latest error (if any) returned by the service or client for that layer, and the AGSLayer. Using the myGraphicsLayerView variable above, use the agsLayer property to get the layer (casting the result to type AGSGraphicsLayer):

AGSGraphicsLayer* myGraphicsLayer =
(AGSGraphicsLayer*)myGraphicsLayerView.agsLayer;

Once you have your Graphics layer, you are ready to add graphic features to it.

Adding Graphic Features

In most cases, the Graphics you add to a Graphics layer will be created by tasks, such as a query, as mentioned above. You can also create Graphics programmatically, in response to a user event, for example. A Graphic is an instance of the AGSGraphic class. To create an AGSGraphic object, you need a geometry which defines the Graphic. The graphic can contain a symbol which provides the display for the graphic, or the GraphicsLayer can be associated with a renderer which decides how all graphics in that layer are displayed.. Optionally, you can have custom attributes associated with the Graphic.

The following code creates a Graphic and adds it to an existing Graphics layer called myGraphicsLayer:

//create a marker symbol to be used by our Graphic
AGSSimpleMarkerSymbol *myMarkerSymbol =
	[AGSSimpleMarkerSymbol simpleMarkerSymbol];
myMarkerSymbol.color = [UIColor blueColor];

//Create an AGSPoint (which inherits from AGSGeometry) that
//defines where the Graphic will be drawn
AGSPoint* myMarkerPoint =
	[AGSPoint pointWithX:-93.2984
		y:44.9409
		spatialReference:self.mapView.spatialReference];

//Create the Graphic, using the symbol and
//geometry created earlier
AGSGraphic* myGraphic =
	[AGSGraphic graphicWithGeometry:myMarkerPoint
		symbol:myMarkerSymbol
		attributes:nil
  infoTemplateDelegate:nil];

//Add the graphic to the Graphics layer
[myGraphicsLayer addGraphic:myGraphic];

//Tell the layer to redraw itself
[myGraphicsLayer dataChanged];

Graphics that are returned as the result of a task are already populated with a geometry but not a symbol, so you will either need to give those Graphics a symbol or apply a renderer to the Graphics layer before adding them to your Graphics layer. To set a symbol into Graphics which have been returned as the result of a Query task you would do the following:

//create a simple fill symbol
AGSSimpleFillSymbol *fillSymbol =
	[AGSSimpleFillSymbol simpleFillSymbol];
fillSymbol.color =
	[[UIColor purpleColor] colorWithAlphaComponent:0.25];
fillSymbol.outline.color = [UIColor darkGrayColor];

//featureSet.features is the result of a Query task.
//It is an array of AGSGraphic objects containing
//geometries, but not symbols.
for (AGSGraphic *graphic in featureSet.features) {
	//set the graphics’s symbol to fillSymbol
	graphic.symbol = fillSymbol;

	//add the graphic to the layer
	[myGraphicsLayer addGraphic:graphic];

}
//Tell the layer to redraw itself
[myGraphicsLayer dataChanged];

See Working with Symbols and Renderers for more information on renderers.

Displaying attribute information in the callout

A graphic can contain custom attributes which provide additional information about the graphic. For instance, a graphic representing a city might have attributes providing information about its total population, average household income, etc. Attributes are basically key-value pairs. A graphic can also optionally contain an infoTemplateDelegate. An infoTemplateDelegate provides a convenient way to specify what information about the graphic should be displayed in a callout.

To have the map automatically display a callout when a user taps on a graphic, you must provide the graphic an infoTemplateDelegate. You can do this in two ways -

If you don't assign an infoTemplateDelegate for a graphic, the map does not display a callout when the user taps on that graphic.

Using a custom infoTemplateDelegate

To create a custom infoTemplateDelegate, you need to make one of your classes adopt the AGSInfoTemplateDelegate protocol.

@interface MyTemplate : NSObject <AGSInfoTemplateDelegate> 
 ... 
@end
An instance of your class must also be set as the graphic's infoTemplateDelegate. This will allow the map to invoke methods on your class and consult it before showing a callout.
AGSGraphic* graphic = ... ;
MyTemplate* template = ... ; 
graphic.infoTemplateDelegate = template;

Finally, your class must implement one or more methods defined in the protocol to specify what content needs to be displayed in the callout. You can provide a tile, detail, and/or image for the callout or you can entirely customize the callout's appearance using a custom view.

@implementation MyTemplate
  ...
  
  - (NSString *)titleForGraphic:(AGSGraphic *)graphic screenPoint:(CGPoint)screen mapPoint:(AGSPoint *)map {
     return @"<title for this graphic>";
  }

  - (NSString *)detailForGraphic:(AGSGraphic *)graphic screenPoint:(CGPoint)screen mapPoint:(AGSPoint *)map {
     return @"<detail for this graphic>";
  }

	 - (UIImage *)imageForGraphic:(AGSGraphic *) graphic screenPoint:(CGPoint) screen mapPoint:(AGSPoint *) mapPoint {  
  	  return [UIImage imageNamed:@"<image_for_this_graphic>"];	
  }

  /* //Only implement if you want to entirely customize the callout
  - (UIView *)customViewForGraphic:(AGSGraphic *) graphic screenPoint:(CGPoint) screen mapPoint:(AGSPoint *) mapPoint {
				//create a view programatically, or load from nib file
  }
  */

@end
NoteNote:

If you use provide custom view for the callout, the title, detail, and image delegate methods are ignored. For more information on how to create views programmatically and using Interface Builder, refer to Apple's View Programming Guide for iOS. You may also refer to Apple's Resource Programming Guide for information on how to load views from nib files.

Using AGSCalloutTemplate

If you're only interested in showing attribute values in the callout, the API provides an AGSCalloutTemplate class which is a convenient implementation of the AGSInfoTemplateDelegate protocol. AGSCalloutTemplate allows you to specify title and detail text for the callout using tokens. Tokens are attribute keys enclosed by ${ } characters. Attributes keys are automatically replaced by attribute values for the graphic when the callout is displayed.

AGSCalloutTemplate* template = [[AGSCalloutTempate alloc] init] ;
template.titleTemplate = @"${CITY_NAME}"; //show the value for attribute key 'CITY_NAME'
template.detailTemplate = @"${POPULATION}"; //show the value for attribute key 'POPULATION'

AGSGraphic* graphic = ... ;
graphic.infoTemplateDelegate = template;

NoteNote:

AGSCalloutTemplate is best suited for attribute values that are strings. It does not provide the ability to localize numbers or dates. Furthermore, it does not provide the flexibilty of specifying an image or a custom view for the callout.

See also

5/9/2012