The MKMapViewDelegate is a protocol that defines how your app can receive updates when changes occur to either the map or any annotations on the map.
As of iOS 6 there are 18 methods available in 7 categories that you can optionally use to receive the updates. Today, I want to cover what each of these methods can do and how you can use them to help bring more functionality to your app.
MKMapView operations often run asynchronously which is why the MKMapViewDelegate comes in handy as actions are processed while you continue using the map and then your app is notified of various changes as available and needed.
This post is not a tutorial as such. Instead, I am just listing the various methods along with details of how they work and what they do. If you want to see some examples of how they are implemented, take a look at the categories listed to the right and select the Map Kit Framework to see tutorials about the various methods.
Responding to Map Position Changes
There are two methods within this category. These are mapVew:regionWillChangeAnimated: and mapView:regionDidChangeAnimated:
Lets first look at the second of these methods. mapView:regionDidChangeAnimated: is called each time the map moves. The arguments here are mapView which lets you know which mapView is sending the update. The other is animated and this lets you know if the last map transition was animated or not. An example use of this could be a map that displays a “Search Here” button if the map moves from a location. If you deviate from a specified area or current location then a button could be made visible. The documentation just indicates that anything done in this method needs to be lightweight because it can be called a number of times when you drag a map.
Just like regionDidChangeAnimated, the former of the two mentioned above is mapView:regionWillChangeAnimated:. This method is similar to the other method but this is called just before the region is about to change. The arguments tell you the map and if the transition will be animated. Again, any implementation of this method needs to be lightweight to ensure the map doesn’t slow down when scrolling. For this reason, I would recommend a lot of if/then type logic to cut out repeating the same instructions which might only need to run once per map scroll.
Loading the Map Data
The next section contains three methods which are mapViewWillStartLoadingMap:, mapViewDidFinishLoadingMap: and mapViewDidFailLoadingMap:withError:
mapViewWillStartLoadingMap: is a method that is called when the specified map is about to start loading data. In English, what that means is that when new map tiles are needed, this method is called. One way this method might be used is to log a time that map tiles were starting to load.
mapViewDidFinishLoadingMap: is called when the tiles of the map have finished loading. This method is called any time that tiles have been downloaded. Even if a map is fully visible on a screen, this method still might be called because iOS might also grab tiles that are immediately surrounding the visible area in preparation for you scrolling.
mapViewDidFailLoadingMap:withError: is used when there is a problem loading map data. It has an NSError argument which provides details of why a portion of the map could not be downloaded. Although this method isn’t marked as required, you might want to consider adding it to increase user experience. Should the map in your app run in to technical problems like a lack of connection, it is good practice to notify the user of a problem. Implementing this method provides the means for you to accomplish just that.
Tracking the User Location
5 methods are available in this section. Each of them related to tracking user location. The methods are:
The mapViewWillStartLocatingUser: method is called when the showsUserLocation property is changed to YES. You might want to implement some code in this method that changes the style of a button to show user tracking is enabled.
mapViewDidStopLocatingUser: is the exact opposite of the method above. When the showsUserLocation property is changed to NO, this method is then called.
mapView:didUpdateUserLocation: is called when the user moves. It is only called if the showsUserLocation is set to YES. This method is not called if the app is running in the background. Usage for this could be to implement the map moving to keep the user in the centre of the map. Each time a new location is tracked, your code could send a message to move the map to the current location coordinates.
mapView:didFailToLocateUserWithError: is used when an error occurs. Perhaps your current location is not available. Implementing this method would provide you with an NSError that you can then act on and inform your user accordingly.
Finally, we have mapView:didChangeUserTrackingMode:animated: which is marked as a required method. This method is called whenever the user tracking mode is changed. When called, it provides the following details:
the mapView which is the map that had its tracking mode change, the mode which provides the mode which was changed to and if it was animated.
The main one here is the mode which we learn in the documentation is an MKUserTrackingMode. This is a typedef which has 3 options available. These are “none”, “Follow” and “FollowWithHeading”. If this was set at None (MKUserTrackingModeNone) then the map does not follow the user location. If set to MKUserTrackingModeFollow then the map will scroll around as the user moves location. If the latter is used which is MKUserTrackingModeFollowWithHeading then the map will rotate as the users heading changes.
This method is called whenever you make that change. A usage example could be updating a button to show what type of tracking mode is currently being used.
Managing Annotation Views
Next up we have the methods for how to handle changes to Annotation Views. The three available methods are:
When mapView:viewForAnnotation is called, it provides the mapView along with the actual annotation object. If an annotation is about to be added to the map, then this method is called when implemented. The annotation could also be the MKUserLocation object (the blue ball).
We use this method to create custom views for our annotations. What we can do is check what type of annotation is being added to the map and then set the view slightly different accordingly. The documentation also warns that we should use the dequeueReusableAnnotationViewWithIdentifier: method to cut down on memory. If a view is already available to use for this particular annotation then we shouldn’t create another view. Instead, we should use an already created view that currently isn’t in use.
This particular method is a little more complex as it requires you check for certain class types and return nil or return something else to show a view. For more details, check out the MKPointAnnotation Tutorial I wrote last week. This covers how to work with this particular delegate method as well as customise pins by changing the colour or changing the annotation pin image.
The mapView:didAddAnnotationViews: is called after annotations have been added to the map. If several are added at the same time, you will have several available in an NSArray. Single annotations are also added in to this NSArray.
This method could be used see what annotations were added and then with the annotations, pull other information from a database in to the view. Of course, you are not just limited to that idea.
mapView:annotationView:calloutAccessoryControlTapped: is used when a callout accessory control is tapped. Each annotation when tapped on, can show a callout that shows a title, subtitle and a left or right accessory. Usually you would put an image in the left and put a button on the right. When the button is tapped, this method is called.
You could use this method to load up a modal view which would populate the view with other information about the pin. Perhaps you have an app that shows properties for sale. When an annotation callout accessory is tapped it could load up details about that particular property or provide a phone number to call about it.
It provides the mapView where the pin was tapped along with the particular annotation as well as the control that was tapped.
Dragging an Annotation View
Next up we just have a single method in the Dragging an Annotation View section. This is:
This method could probably do with a with a tutorial in itself as it isn’t as straight forward as some of the other methods in this delegate class. This tutorial will come at a later date, but for now here is the quick explanation of why this method would be called.
When you tap and hold on a pin, you can begin to drag that pin to another location. When that happens, this method is called. As always, you have the mapView where the pin was tapped. You also have the Annotation View. You also have the drag state of the newState and the same for the oldState which could be one of five options which includes None, Starting, Dragging, Cancelling, Ending. As can be seen, this explanation probably doesn’t make it crystal clear what is actually happening when you drag a pin. I will address that in a tutorial and update this post to show an example of how dragging a pin works.
Selecting Annotation Views
The methods available in this section are:
These two are relatively simple to understand. The first method is called when you tap on an annotation to select it. The latter is called when an annotation is deselected. What you get available is the map where the annotation was tapped as well as the specific annotation.
Depending on what you want to do after an annotation is selected or deselected is up to you. You would just implement these two methods and add your code in as needed.
Managing Overlay Views
Last for today is managing overlay views. We haven’t looked in to overlays yet on DevFright, but to give you a quick summary, two methods are available:
The mapView:viewForOverlay: method is implemented when you are working with overlays. What we have is the specific mapView, just like examples above, and also an MKOverlay. This method is called when an overlay is about to be added to the screen. The overlay argument is used so that we can provide it with a view to put the overlay object on to.
Just like the draggable annotation methods above, this method is also quite complicated and needs to be described in a full tutorial… of which I will add in the coming weeks and update here accordingly.
Last of all, we have the mapView:didAddOverlayViews: method which works in a similar way to the mapView:didAddAnnotations: method discussed above. When overlays are added to the screen, this method is called and provides the specific overlays that were added.
In our tutorials we have looked at a few of these delegate methods. We will be adding more over time, so please check the Map View Framework category and specifically the MKMapViewDelegate category. Any tutorials that use this delegate will be marked and added to that category.
Other than providing information here, I can only suggest that you dive in to the code and test for yourself. When doing that, you will learn far more because you will see it for yourself rather than have me explain it to you. By all means, if you get stuck, go ahead and use the tutorials here. They are there to help you out when needed.