MKMapView - sKarumba/TourApp GitHub Wiki
#MKMapView and MKMapView Delegate Tutorial
Adding a map to your app is done through the MKMapView class. Put simply, the class allows an object to be created which provides an embeddable map interface. Dragging a map on to a View Controller on the storyboard is enough to initialise this object and display an interactive map on the screen which you can pinch and zoom as well as pan around. Adding more functions such as showing your current location, showing a different map type as well as other options can either be done through the attributes inspector or programatically.
##MKMapViewDelegate Just like the CLLocationManagerDelegate, this protocol defines the rules on how certain updates are received. There are a number of methods found within the delegate protocol which allow you to respond to map position changes, load map data, track user location as well as manage annotation views to name a few.
I will just cover two of the methods today. The first is mapView:regionDidChangeAnimated: and the second is mapView:didUpdateUserLocation:.
###mapView:regionDidChangeAnimated: If the MKMapView changes, this method is called.
I have added a UIButton to the top of the view in the centre. I also connected it up as an IBOutlet. For the purpose of this tutorial there will be no associated IBAction with this button. What we are going to do is make the button disappear when the map is zoomed in to the current location. If you move the map away, we will unhide the button. The idea is that you might have an app where you search for something. If searching your current location you don’t need to see the button, but if you want to scroll to somewhere else to perform the same search, the button can then appear.
In viewDidLoad add the line:
self.searchButton.hidden = YES;
In the zoomToCurrentLocation method add the following line:
self.searchButton.hidden = YES;
Add the delegate method as follows:
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
self.searchButton.hidden = NO;
}
What this is doing is revealing the search button each time the map moves away from the current location. To get it working more smoothly you will need to add a bit more logic in to the method, but in a clumsy way it shows one way of how the delegate method can be used.
Run the app now and you will see how the search here button appears and then disappears when moved to the current location.
###mapView:didUpdateUserLocation:
Finally, we’ll look at how the mapView:didUpdateUserLocation method works. We add the delegate method to the code and each time the user moves, the method is called. For this example, we will move to the new location but without requiring the zoom… ie, we will use the setCenterCoordinate:animated: method from the MKMapView.
The code for doing this is as follows:
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:userLocation.coordinate animated:YES];
}
Each time the current location moves, we can use the setCenterCoordinate:animated: method to automatically animate to the new location. To see this working in the simulator, select Debug > Location > City Bike Ride and the map will follow the user keeping the blue dot in the centre.
##MKMapView Tutorial As usual, lets start with a Single View Application. When done, add the MapKit.framework to the project.
When done, go to ViewController.h and import MapKit and add the syntax for MKMapViewDelegate as below:
#import <MapKit/MapKit.h>
@interface ViewController : UIViewController <MKMapViewDelegate>
Go to the iPhone storyboard and drag a UIToolBar and MKMapView in to the view and resize to fit. I also changed the text of the UIBarButtonItem to Current Location which will be used later in this tutorial.
When you have added these to your app you should be able to run the app without errors.
The next step is to connect up your map as an IBOutlet. To do this, CRTL+drag from the MapView to the header file.
I called my map mapView and then clicked the Connect button to set up the @property. The MKMapView is now a property that we can set and get information from.
Because we dragged out an MKMapView in to the storyboard we don’t need to alloc/init it because Xcode takes care of that for us. What this means is that we can begin manipulating the map by setting @properties such as current location.
We will try manipulating a few of the properties where possible or read information from those properties when they are readonly. The available properties are:
- mapType
- zoomEnabled
- scrollEnabled
- delegate (we will set this later).
- region
- centerCoordinate
- visibleMapRect
- showsUserLocation
- userLocationVisible
- userLocation
###mapType Property Lets begin by looking at the mapType property. There are three options that are typedef’d here. These are:
- MKMapTypeStandard
- MKMapTypeSatellite
- MKMapTypeHybrid
You can test how this works by simply adding a line of code in viewDidLoad:
self.mapView.mapType = MKMapTypeHybrid;
In some cases you will likely want to let the user choose the style of map. Lets have a quick look at how this can be done. What we will do is add a UISegmentedControl over the map and allow for 3 segments. We will set the style to Bar. The segments will be Standard, Satellite and Hybrid as shown in app.
###zoomEnabled and scrollEnabled I wont go in to too much detail about the zoomEnabled and scrollEndabled properties. You will have noticed in the attributes inspector that you can set these properties. Typically I would do this here rather than in code, but if you wanted to set one of these properties in code you would do this:
self.mapView.zoomEnabled = NO;
Any property you set in code will automatically override any setting in the attributes inspector. If you want to try add this line of code you can put it in the viewDidLoad method and try pinch the map.
The same also applies for scrollEnabled. You just specify that as a property on the end of self.mapView.scrollEnabled and set it to YES or NO as requited.
###Accessing the Device’s Current Location We have three properties available in this section. These are:
- showsUserLocation
- userLocationVisible
- userLocation
If we set showsUserLocation to YES then we will be prompted to allow location information on the device. However, setting this property to YES does not mean you will see the blue ball showing your location. It simply means that “if” your location is available it will show it. Setting the property in this way will also override the attributes inspector setting.
###Manipulating the Visible Portion of the Map
Moving on to the next section we now have 3 properties available and 4 methods which each allow you to specify regions, coordinates as well as programatically change the part of the map that is in the view.
####The region property
The region property is what you get information from about the region that is currently visible on the map. It provides a centre coordinate as well as a span in both vertical and horizontal directions.
We then use the following code to create a region:
- (IBAction)zoomToCurrentLocation:(UIBarButtonItem *)sender {
float spanX = 0.00725;
float spanY = 0.00725;
MKCoordinateRegion region;
region.center.latitude = self.mapView.userLocation.coordinate.latitude;
region.center.longitude = self.mapView.userLocation.coordinate.longitude;
region.span.latitudeDelta = spanX;
region.span.longitudeDelta = spanY;
}
###setCenterCoordinate:animated:
If you are happy with the zoom level and just want to reposition the map to somewhere else, you can call this method and provide it with a new coordinate to move to. I wont go in to the syntax here as it is similar to the setRegion method. We still need to provide it an argument and in this case we specify a coordinate in CLLocation2D format.
###setVisibleMapRect:animated:
This method is similar to the setCentreCoordinate:animated: method above but instead of setting the coordinate, you are setting the visible portion of the map to display.
Likewise, the setVisibleMapRect:edgePadding:animated: method does a similar thing except it adds some padding around the edges.
##Complete code ###Header File
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface ViewController : UIViewController <MKMapViewDelegate>
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (weak, nonatomic) IBOutlet UIButton *searchButton;
@end
###Implementation
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
self.mapView.mapType = MKMapTypeStandard;
self.mapView.showsUserLocation = YES;
self.searchButton.hidden = YES;
}
- (IBAction)setMapType:(UISegmentedControl *)sender {
switch (sender.selectedSegmentIndex) {
case 0:
self.mapView.mapType = MKMapTypeStandard;
break;
case 1:
self.mapView.mapType = MKMapTypeSatellite;
break;
case 2:
self.mapView.mapType = MKMapTypeHybrid;
break;
default:
break;
}
}
- (IBAction)zoomToCurrentLocation:(UIBarButtonItem *)sender {
float spanX = 0.00725;
float spanY = 0.00725;
MKCoordinateRegion region;
region.center.latitude = self.mapView.userLocation.coordinate.latitude;
region.center.longitude = self.mapView.userLocation.coordinate.longitude;
region.span.latitudeDelta = spanX;
region.span.longitudeDelta = spanY;
self.searchButton.hidden = YES;
[self.mapView setRegion:region animated:YES];
}
-(void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
self.searchButton.hidden = NO;
}
-(void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation {
[self.mapView setCenterCoordinate:userLocation.coordinate animated:YES];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end