Skip To Content

Displaying web map popups

In this topic

A map is a more than a picture. It conveys information about your surroundings and helps make decisions. But a picture does not tell the whole story. There is a wealth of information hidden behind the image. Popups are a great way to reveal information about features on a map, such as a business, location, natural phenomena, or geopolitical entity.

Learn more about popups

You can configure popups when authoring web maps on A popup is associated with a specific layer in a web map and describes how information about features in that layer should be presented. When configuring popups, you can choose which fields to display, give them aliases, specify formatting for numbers and dates, configure charts, and so on.

Learn more about authoring web maps with popups

Displaying information using popups

AGSPopupsContainerViewController is the main class you need to use to display popups in your applications. It provides the basic user interface (UI) and behavior for displaying and editing information about features in a popup. It is a container and manages a number of view controllers behind the scenes, each of which is designed for a specific purpose, such as editing an attribute, displaying media, managing attachments, and so on. AGSPopupsContainerViewController manages transitions between these view controllers and displays the relevant one depending on what the user intends to do.

Displaying popups requires the following two steps:

  1. Instantiating AGSPopupsContainerViewController
  2. Displaying AGSPopupsContainerViewController

1. Instantiating AGSPopupsContainerViewController

To instantiate AGSPopupsContainerViewController, you need to have a reference to the feature(s) with information you want to display. The feature(s) can be chosen by a user in the following ways:

  • Tapping the map.
  • Tapping the accessory button in the callout for a specific feature.
  • Selecting an option from a table that lists some features.

For popups defined in a web map, you can instantiate an AGSPopupsContainerViewController using initWithWebMap:feature:usingNavigationControllerStack: by passing in a reference to the web map that contains the popup definition, and the feature for which the popup needs to be displayed.

let webMap:AGSWebMap = ...
let graphic:AGSGraphic = ...
let popupVC = AGSPopupsContainerViewController(webMap: webMap, forFeature: graphic, usingNavigationControllerStack: false)

To display popups for more than one feature at the same time, you can instantiate AGSPopupsContainerViewController using initWithPopups:usingNavigationControllerStack: by passing in an array of popups. Each popup is represented by an AGSPopup object, which contains references to a feature and the popup definition for that feature.

let popups = NSMutableArray()
//The popup definition
let popupInfo:AGSPopupInfo = ...
//The feature to be displayed in a popup
let graphic:AGSGraphic = ...
//Associate the popup definition with the feature to get a popup
let popup = AGSPopup(graphic: graphic, popupInfo: popupInfo)
//Pass the list of popups to the view controller
let popupVC = AGSPopupsContainerViewController(popups: popups, usingNavigationControllerStack: false)

A popup definition is represented by an AGSPopupInfo object. You can either create popup definitions programmatically, or retrieve them from a web map.

Retrieving popup definitions from a web map

You can retrieve popup definitions from a web map provided you know which layer or service the popup is associated with. For example, if you have a reference to a graphic belonging to a feature layer, you can retrieve the popup definition for that graphic as follows:

let graphic:AGSGraphic = ... //graphic in a feature layer for which we want popup definition
let popupInfo:AGSPopupInfo =  self.webmap.popupInfoForFeatureLayer(graphic.layer)

There may be times when you cannot easily discern which layer a graphic belongs to, for instance, when the graphic is a result of a query. In such cases, the AGSGraphic object's layer property will be nil and you need to manually keep track of which layer in the web map contains a popup definition for that feature. In such cases, you can use the URL of the service to retrieve the popup definition from a web map.

let serviceURL:NSURL = ... //the service that the graphic belongs to
let layerId:Int = ... //sub-layer in the service containing the popup definition
let popupInfo:AGSPopupInfo =  self.webmap.popupInfoForMapServiceLayerWithURL(serviceURL, sublayerId: layerId)

Creating popup definitions programmatically

If you are directly consuming services in your application and not using a web map, or if your web map does not contain popup definitions, you can create popup definitions programmatically to display and/or edit information about features using popups. A popup definition is represented by an AGSPopupInfo object and contains the following information:

  • Whether a user should be allowed to edit or delete the feature.
  • Which attributes of the feature the popup should display, whether the attributes are editable, how to format numbers and dates, and so on.
  • Whether the popup should show attachments for the feature.
  • What media, such as charts and images, should be displayed for the feature.

You can instantiate a new AGSPopupInfo object using the convenience constructor popupInfoForGraphic: by passing in an AGSGraphic object. If the graphic belongs to a feature layer (that is, the AGSGraphic object's layer property points to a feature layer), the constructor will consult the metadata on the feature layer and create a popup definition with appropriate defaults for the information listed above. Otherwise, it will inspect the attributes of the graphic to create a basic popup definition that you can then tweak.

Displaying AGSPopupsContainerViewController

AGSPopupsContainerViewController is a subclass of UIViewController. As with any Cocoa Touch view controller, it can be displayed in a variety of ways depending on the device being targeted. A full discussion of the ways in which you can display view controllers is outside the scope of this topic. See the Apple View Controller Programming Guide for more information. Following are some common ways in which you can display AGSPopupsContainerViewController.

iPhone and iPod Touch

Given the limited screen size on iPhone and iPod Touch devices, the recommended way to display AGSPopupsContainerViewController on such devices is modally-covering the entire screen and obscuring contents beneath it.

Example of a popup on iPhone
Popup displayed modally on an iPhone
popupVC.modalTransitionStyle =  .CoverVertical
self.presentViewController(popupVC, animated: true, completion: nil)

For more information on best practices for displaying views modally, see the Modal View section of iOS Human Interface Guidelines.


You have more options for displaying popups on an iPad. You can continue to display AGSPopupsContainerViewController modally; however, you would typically assign it a specific modalPresentationStyle to display it, for example, as a form sheet or page sheet.

Popup displayed modally in a form sheet on an iPad
Popup displayed modally in a page sheet on an iPad
popupVC.modalTransitionStyle = .FlipHorizontal
popupVC.modalPresentationStyle = .FormSheet
self.presentViewController(popupVC, animated: true, completion: nil)

You can also embed the AGSPopupsContainerViewController view in other views such as a map's callout, a popover, a split pane, or some other view in your application.

let popupVC:AGSPopupsContainerViewController = ...
//showing popups in a custom callout
popupVC.view.frame = CGRect(0,0,192,288)
popupVC.actionSheetContainerView = self.view
popupVC.modalPresenter = self
self.mapView.callout.customView = popupVC.view
self.mapView.callout.showCalloutAt(location, screenOffset: offset, animated: true)

//showing popups in a popover
let popover = UIPopoverController(contentViewController: popupVC)
popover.setPopoverContentSize(CGSize(320, 480), animated: true)
popover.presentPopoverFromRect(location, inView: self.view, permittedArrowDirections: .Any, animated: true)

When you do so, only the initial popup view will be displayed within that view. Subsequent popup views will still be displayed modally, covering the entire screen. This may or may not be consistent with your application's user experience. If you want subsequent views to be displayed differently, for example, within the same view or inside another view, you need to implement the popupsContainer:wantsToShowViewController:ofType:fromViewController:atRect: and popupsContainer:wantsToHideViewController:ofType: delegate methods and take the responsibility of transitioning from one popup view to another.

The following code snippet demonstrates how to display subsequent popup views in a form sheet on an iPad:

func popupsContainer(popupsContainer: AGSPopupsContainer!, wantsToShowViewController svc: UIViewController!, ofType viewType: AGSPopupViewType, fromViewController fvc: UIViewController!, atRect rect: CGRect) {
 if(viewType == .UIImagePicker){
  let popover = UIPopoverController(contentViewController: svc)
  popover.setPopoverContentSize(CGSize(200, 300), animated: true)
  popover.presentPopoverFromRect(rect, inView: fvc.view, permittedArrowDirections: .Any, animated: true)
 }else {
  svc.modalTransitionStyle = .FlipHorizontal
  svc.modalPresentationStyle = .FormSheet
  self.presentViewController(svc, animated: true, completion: nil)
func popupsContainer(popupsContainer: AGSPopupsContainer!, wantsToHideViewController vc: UIViewController!, ofType viewType: AGSPopupViewType) {
	self.dismissViewControllerAnimated(true, completion: nil)

Handling user interaction

AGSPopupsContainerViewController also handles most user interaction with the UI, such as bringing up an appropriate keyboard when the user starts editing a numeric attribute, allowing the user to take a picture or browse the photo library for attachments, displaying action sheets, and so on.

In many cases, AGSPopupsContainerViewController does not implement any default behavior, but instead, provides hooks for you to handle user interaction on your own. For example, when a user taps the Done button, AGSPopupsContainerViewController informs its delegate so that it can appropriately dismiss the popup. To respond to such user interaction, you must set one of your classes as the AGSPopupsContainerViewController's delegate. You do this by making your class (typically the view controller that displays the popups) adopt the AGSPopupsContainerDelegate protocol.

class MyViewController: UIViewController, AGSPopupsContainerDelegate {

An instance of your class must also be set as the view controller's delegate. This allows it to invoke methods on your class in response to user interaction.

popupVC.delegate = self

Finally, your class should implement one or more methods defined in the protocol that pertain to the user interaction you want to handle.

class MyViewController: UIViewController, AGSPopupsContainerDelegate {

 func popupsContainerDidFinishViewingPopups(popupsContainer: AGSPopupsContainer!) {
  self.dismissViewControllerAnimated(true, completion: nil)

Editing information using popups

AGSPopupsContainerViewController provides a UI that makes it easy to collect information about a feature from the user. AGSPopupsContainerViewController informs its delegate of various events as the user attempts to edit a feature.

Editing workflow in popup
Editing workflow

Editing attributes

The UI for attribute editing automatically applies rules governing the legal values permitted for a feature's attribute. Such rules include enforcement of coded value domains, range domains, length, data type, whether the attribute is read-only, and so on. Information regarding the validation that needs to be applied to each attribute is gathered from the feature layer to which the feature belongs.

Attribute with coded value domain
Attribute with coded-value domain
Date attribute
Date attribute
Text attribute with length validation
Text attribute with length validation

Editing attachments

Popups make it easy to edit media attachments for a feature. Users can attach photos and videos to features either by choosing an existing item from the device's photos library or by using the device's camera, if one is available. You do not need to write any code to implement this functionality, it is provided by the AGSPopupsContainerViewController.

Editing existing attachment
Adding new attachment

Editing geometry

AGSPopupsContainerViewController does not provide any default UI for capturing or editing a feature's geometry. Instead, it informs its delegate via the popupsContainer:readyToEditGeometry:forPopup: method when a user initiates the geometry editing workflow. The geometry that needs to be edited is passed into the method.

It is your responsibility to implement this method and present an appropriate view that will allow the user to edit an existing feature's geometry, or to create a new geometry. You can use AGSSketchGraphicsLayer to let a user create or edit geometries interactively.

Learn more about using the Sketch layer

You must also implement the popupsContainer:wantsNewMutableGeometryForPopup: delegate method for cases when the graphic being editing does not have a geometry, for instance, when the user tries to create a new feature. In this method, you must return an empty geometry that is mutable. AGSPopupsContainerViewController will pass this geometry back to you in popupsContainer:readyToEditGeometry:forPopup: and also track its status so that the Done button in the popups view controller can be appropriately enabled or disabled depending on whether or not the sketched geometry is valid.

func popupsContainer(popupsContainer: AGSPopupsContainer!, wantsNewMutableGeometryForPopup popup: AGSPopup!) -> AGSGeometry! {
 //This method is called only if the user tries to create a new feature, or edit a feature that doesnt contain a geometry
 //We return an empty mutable geometry of the type that our feature layer uses
 return AGSMutableGeometryFromType((popup.graphic.layer as AGSFeatureLayer).geometryType, self.mapView.spatialReference)
func popupsContainer(popupsContainer: AGSPopupsContainer!, readyToEditGeometry geometry: AGSGeometry!, forPopup popup: AGSPopup!) {
 //On an iPhone, you will need to dismiss the popup view which was displayed modally
 self.dismissViewControllerAnimated(true, completion: nil)
 //and present another view that will allow the user to sketch on a map.
 //On an iPad, you may not need to dismiss the popup view if it can be displayed side-by-side
 //with the view that the user will sketch on
 //In that view, get a reference to the sketch layer in the map
 let sketchLyr:AGSSketchGraphicsLayer = ...
 //Activate the sketch layer in preparation for the user to sketch
 self.mapView.touchDelegate = sketchLyr
 //Use the geometry passed to us as the starting point for the sketch
 sketchLyr.geometry = geometry

Persisting edits

All edits made to a feature via the popup UI exist only locally on the device. If the user closes your application, or if the device powers down, the edits will be lost. AGSPopupsContainerViewController informs its delegate whenever a user edits or deletes a feature. For example, the popupsContainer:didFinishEditingForPopup: delegate method is invoked when a user has finished editing a feature, and the popupsContainer:wantsToDeleteForPopup: method is invoked when the user deletes a feature. It is your responsibility to implement such delegate methods and appropriately commit edits to the server or persist them some other way.

You would typically use AGSFeatureLayer to commit the edits to an ArcGIS Feature Service. You can use methods on the feature layer to commit edits made to a feature's geometry and attributes. You can also use the feature layer to commit edits made to a feature's attachments; however, AGSAttachmentManager is easier to use and provides a simple, coarse-grained API that is built on top of the feature layer specifically to manage attachments.

func popupsContainer(popupsContainer: AGSPopupsContainer!, didFinishEditingForPopup popup: AGSPopup!) {
 var featureLayer = popup.graphic.layer as AGSFeatureLayer
 // simplify & normalize the geometry associated with the feature in case it was sketched by the user
 popup.graphic.geometry = AGSGeometryEngine.defaultGeometryEngine().simplifyGeometry(popup.graphic.geometry)
 popup.graphic.geometry = AGSGeometryEngine.defaultGeometryEngine().normalizeCentralMeridianOfGeometry(popup.graphic.geometry)
 featureLayer.editingDelegate = self
 let oid = featureLayer.objectIdForFeature(popup.graphic)
 if oid > 0 {
  //feature has a valid objectid, this means it exists on the server
  //and we simply update the existing feature
 else {
  //objectid does not exist, this means we need to add it as a new feature
 //we will post attachments below when the updates succeed
func featureLayer(featureLayer: AGSFeatureLayer!, operation op: NSOperation!, didFeatureEditsWithResults editResults: AGSFeatureLayerEditResults!) {
 //if edits pertaining to the feature were successful...
 if editResults.addResults.count > 0 || editResults.updateResults.count > 0 {
  //...we post edits to the attachments
  let attMgr = featureLayer.attachmentManagerForFeature(popupVC.currentPopup.graphic)
  attMgr.delegate = self
  if attMgr.hasLocalEdits() {
func featureLayer(featureLayer: AGSFeatureLayer!, operation op: NSOperation!, didFailFeatureEditsWithError error: NSError!) {
 println("Could not commit edits because: \(error.localizedDescription)")
 //Hide the network activity indicator
 //Display an alert to the user
 //Restart editing the popup
func attachmentManager(attachmentManager: AGSAttachmentManager!, didPostLocalEditsToServer attachmentsPosted: [AnyObject]!) {
 //loop through all attachments looking for failures
 var anyFailure = false
 for attachment in attachmentsPosted as [AGSAttachment] {
  if attachment.networkError != nil || attachment.editResultError != nil {
   anyFailure = true
   var reason:String!
   if attachment.networkError != nil {
    reason = attachment.networkError.localizedDescription
   else if attachment.editResultError != nil {
    reason = attachment.editResultError.errorDescription
   println("Attachment \( could not be synced with server because \(reason)")
 if anyFailure {
  //warn user of error

Learn more about editing with ArcGIS Feature Layer

Learn more about working with an attachment manager

Customizing the UI


You can change the color of the toolbars in the popup views by modifying the style property on AGSPopupsContainerViewController.

let popupVC:AGSPopupsContainerViewController = ... = .CustomColor
popupVC.styleColor = UIColor.lightGrayColor()

Paging style

You can change the paging style by modifying the pagingStyle property on AGSPopupsContainerViewController. You can choose between two styles: page control or toolbar.

Page control style
Toolbar style

In both styles, you swipe to view the next or previous popup. The page control is more suitable if you're only displaying a small number of popups (for instance, up to 10) simultaneously. The toolbar is more appropriate for displaying a larger number of popups together, as you can easily jump to the beginning and the end of the list.

Editing style

You can change the editing style by modifying the editingStyle property on AGSPopupsContainerViewController. You can choose between two styles: inline geometry editing or geometry editing tool.

Geometry editing tool style
Geometry editing tool style
Inline goemetry editing style
Inline geometry editing style

With the geometry editing tool style, the user needs to explicitly begin the act of collecting or modifying a feature's geometry by tapping the tool. You need to handle this event by appropriately displaying a view that will allow the user to sketch a geometry. Although this style can be used for both iPhone and iPad applications, it is more appropriate for iPhone applications where the popup view covers the entire screen and the user needs to click the geometry tool to bring up another view where he or she can sketch on a map.

With the inline geometry editing style, a user does not need to explicitly initiate the act of collecting or modifying a feature's geometry. It is assumed that the application is already in a state where the user can begin sketching on the map. This style is more appropriate for iPad applications where the popup may be displayed side-by-side with the map, and thus the user can start sketching at any time.

Apart from changing the editing style, you can also specify, for each individual popup, whether the feature can be edited, which fields are editable, whether its geometry can be modified, if it can be deleted, and whether it should show its attachments. These can be controlled by modifying the relevant properties on AGSPopupInfo.

Custom action

You can replace the right bar button bar button in the top toolbar of the popup view by setting the actionButton property on AGSPopupsContainerViewController. You can perform any action when the button is clicked, such as zooming into the feature being displayed in the popup, or displaying a custom action sheet with further options.

Localizing the UI

The text displayed in the popup views has been externalized into a strings file called Localizable.strings to make localization easy. This file is included in the ArcGIS.bundle file under a language specific .lproj subdirectory. ArcGIS.bundle is installed under ${HOME}/Library/SDKs/ArcGIS/iOS/ArcGIS.framework/Resources. Translations for the following languages are included by default:

  • Arabic
  • Chinese
  • Czech
  • Danish
  • Dutch
  • English
  • Estonian
  • Finnish
  • French
  • Hebrew
  • German
  • Italian
  • Japanese
  • Korean
  • Lithuanian
  • Latvian
  • Norwegian
  • Polish
  • Portuguese
  • Romanian
  • Russian
  • Spanish
  • Swedish

When displaying the UI, the popup view controllers automatically pick up the appropriate translations from ArcGIS.bundle depending on the language setting of the device. For this to happen, however, your project must contain at least one .nib file localized for that language. To localize a .nib file in Xcode 4, select it in the left pane, and add the desired languages to the Localization section in the info pane on the right.

Adding localizable strings

If the language you're targeting is one of those specified in the list above, you don't need to do anything additional because the translations for those languages are already provided with the SDK. If you're targeting another language, you need to provide the translated strings for that language.

To add translations for additional languages, you need to create a new language specific .lproj subdirectory in ArcGIS.bundle. The subdirectory's name must be in line with Apple's naming convention using a Language ID and, optionally, a Locale ID. Finally, you need to add a translated version of Localizable.strings to this subdirectory. This strings file must contain translations for each key specified in the strings files provided with the SDK. The popup view controllers can then automatically pick up your translations based on the language setting of the device.

For more information about localizing string resources, see the Apple Internationalization Programming topics and Resource Programming Guide.

See also