Playing With MapKit
Part 1 MapKit Tutorials
Part 2 Reverse Geocoding and Custom Annotation Callouts
Part 4 Race Conditions, Other Bugs, and Wrap-up
A main difference between a smartphone and any other phone is that smart phones can tell you where they are. What does location tell you? Location provides context about what you are doing, where you’re going, and by extension, who you are.
Apple makes it easy to show this information on a map with MapKit.
At the Flatiron School, I got a good foundation for learning frameworks, but hadn’t worked with MapKit yet. Since it felt core to many mobile apps, I decided to explore it.
Here is a demo of the finished app. All the code is on Github.
First Stop on the MapKit Train: Tutorials
Whenever I get into a new framework, I learn best by jumping right in and doing it. Tutorials are a great way to get started. They help me focus by limiting scope and stay productive before my natural curiosity wants to go down some rabbit holes. I also don’t want to reinvent the wheel.
If I have seen further it is by standing on the shoulders of giants.
Searching for iOS Mapkit tutorial, I find some by Ray Wenderlich, TechTopia, and AppCoda. All three have been really useful in the past. I went with the TechTopia one because it was written about iOS 7.
Tutorial 1: Make a MKMapView and Show the Current Location
- Add the MapKit framework
- Make a MKMapView with Storyboards and import MKMapView
- Assign the MKMapViewDelegate to the view controller
- Set the MKMapView to show the current Location
- *Not In Tutorial: Asking for Permission to Access User Location
- *Set the location in Simulator
- Zoom in on the MKMapView by changing the region
- Change the MKMapView type
- Update the MKMapView using the MKMapViewDelegate methods
*Most of this is already well explained in the tutorial. I want to point out two bumps on the road.
1. Simulator does not have a GPS, so you have to set the location by the menu bar: Debug -> Location -> Custom Location.
Google maps will give you latitude and longitude for any location if you click on the map. If it’s an icon, right click and click “What’s here?”.
Note: Sometimes the location doesn’t show up in the simulator the first time I run it. When I run it on another sized simulator, it works. Don’t know why this is.
2. More importantly, iOS 8 requires that you get permission to use the user location.
How to Get the Current Location Authorization Status
CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
CLAuthorizationStatus is an enum of
- kCLAuthorizationStatusNotDetermined = 0
- kCLAuthorizationStatusRestricted = 1
- kCLAuthorizationStatusDenied = 2
- kCLAuthorizationStatusAuthorized = 3 (Deprecated iOS 8)
- kCLAuthorizationStatusAuthorizedAlways = kCLAuthorizationStatusAuthorized = 4
- kCLAuthorizationStatusAuthorizedWhenInUse = 5
Statuses start out as kCLAuthorizationStatusNotDetermined
How to Ask the User for Location Authorization
If you want current location data only when the customer is using the app:
If you want current location data even when the customer is not using the app:
Here I have my CLLocationManager as a property of the class. Change self.locationManager to the name of your locationManager.
How to Add Properties to your Info.plist
Even though you think you’ve requested authorization, you are likely still missing one more piece.
In the Supporting Files Folder of your app directory, there is a Info.plist file.
It’s a properties list that your app uses through out the app.
You have to have a property in there for NSLocationWhenInUseUsageDescription (or NSLocationAlwaysUsageDescription depending on which permission your asking for), which tells the customer why you are asking for their location information.
How to tell if you received location authorization
There is a delegate method for the location manager that tell you when the authorization status changes.
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus) status
The status here is the CLAuthorizationStatus.
Remember to set the locationManager’s delegate to the class that implements the delegate method. In this case, I’ve set the delegate to self inside my view controller.
self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self;
Tutorial 2: Add Local Search with MKLocalSearchRequest and Display Results as MKPointAnnotation
- Add a textField so the user can input search terms
- Add an IB action for the textFieldReturn to call the performSearch method
- Add a performSearch method that sends a MKLocalSearchRequest and handles the MKLocalSearchResponse by parsing its array of MKMapItems into MKPointAnnotations
This was pretty straight forward. Apple makes the searches super easy by giving you completion handlers.
How to Perform a Local Search
You just need a MKLocalSearch object and a MKLocalSearchRequest object.
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
- (void)startWithCompletionHandler: (
Tutorial 3: Find Directions with MKDirectionsRequest and Draw Them on the Map
- Add a ResultsTableViewController to show the names and phone numbers of the venues
- Add a RouteViewController to show the route to the destination
- Set up a MKDirectionsRequest with a source and destination in the RouteViewController
- Make a MKDirections instance that calculates directions
- Pass the MKDirectionsResponse to a showRoute method that adds an overlay of the polylines of the MKRoutes in the response to the map
- Set up how the overlay will look
The tutorial sets up a custom UITableViewCell, but that’s not really necessary because you can set the title as the name and the subtitle to the phoneNumber.
Why am I in the middle of the ocean?
It’s important that you implement the MKMapView delegate method
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation: (MKUserLocation *)userLocation
Otherwise, you’ll find yourself in the ocean next to West Africa a lot (that’s what happens when your coordinates are (0,0)).
How to Get Directions
Make an instance of MKDirections and call:
- (void)calculateDirectionsWithCompletionHandler: (