21 Jul
Step 1 – Create a new View Based application in XCode. Name is whatever you like. For now on I will refer to your projects name as ProdjectName.
Step 2 - Add MapKit.framework and CoreLocation.framework to the project.
Step 3 – Open the ProdjectNameViewController.h. Here you will need to add an outlet to the map view and create a CLLocationManager and CLLocation. The CLLocation will keep up with our current location.
1: @interface ProdjectNameViewController : UIViewController <UIAlertViewDelegate, CLLocationManagerDelegate, MKMapViewDelegate> {
2: IBOutlet MKMapView* myMapView;
3:
4: CLLocationManager* locationManager;
5: CLLocation* currentLocation;
6: }
7:
8: @property (nonatomic, retain) IBOutlet MKMapView* myMapView;
9:
10: @property (nonatomic, retain) CLLocationManager* locationManager;
11: @property (nonatomic, retain) CLLocation* currentLocation;
12:
13:
14: @end
You may want to go ahead and open your ProdjectNameView.xib and hook up the map view to the view controller outlet at this time.Step 4 – Open the ProdjectNameViewController.m. You will need to synthesize the newly created variables and create your delegate methods for the Map View and Location Manager. You can see the full code in the attached project. In the -viewDidLoad method I set the map view and location manager delegates to self, add a few checks for the location services, and start updating to the users current location.
1: - (void)viewDidLoad {
2: [super viewDidLoad];
3:
4: [myMapView setMapType:MKMapTypeHybrid];
5: [myMapView setDelegate:self];
6:
7: locationManager = [[CLLocationManager alloc] init];
8: [locationManager setDelegate:self];
9: [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
10: if(!locationManager.locationServicesEnabled) {
11: UIAlertView *locationServiceDisabledAlert = [[UIAlertView alloc]
12: initWithTitle:@"Location Services Disabled"
13: message:@"Location Service is disabled on this device. To enable Location Services go to Settings -> General and set the Location Services switch to ON"
14: delegate:self
15: cancelButtonTitle:@"Ok"
16: otherButtonTitles:nil];
17: [locationServiceDisabledAlert show];
18: [locationServiceDisabledAlert release];
19: }
20:
21: [locationManager startUpdatingLocation];
22: }
Step 5 – Let’s go ahead and create 2 new files. The first file will be a MKPlaceMark which we will call CurrentLocationAnnotation and the other will be a MKAnnotationView subclass which we can call AnnotationView.CurrentLocationAnnotation.h:
1: #import <MapKit/MapKit.h>
2:
3: @interface CurrentLocationAnnotation : MKPlacemark {
4:
5: }
6:
7: @property (nonatomic, readwrite, assign) CLLocationCoordinate2D coordinate;
8:
9: @property (nonatomic, retain) NSString *title;
10: @property (nonatomic, retain) NSString *subtitle;
11:
12: @end
CurrentLocationAnnotation.m:1: #import "CurrentLocationAnnotation.h"
2:
3:
4: @implementation CurrentLocationAnnotation
5: @synthesize coordinate;
6: @synthesize title;
7: @synthesize subtitle;
8:
9: - (id)initWithCoordinate:(CLLocationCoordinate2D)coord addressDictionary:(NSDictionary *)addressDictionary {
10:
11: if ((self = [super initWithCoordinate:coord addressDictionary:addressDictionary])) {
12: // NOTE: self.coordinate is now different from super.coordinate, since we re-declare this property in header,
13: // self.coordinate and super.coordinate don't share same ivar anymore.
14: self.coordinate = coord;
15: }
16: return self;
17: }
18: @end
AnnotationView.h:1: #import <MapKit/MapKit.h>
2:
3: @interface AnnotationView : MKAnnotationView {
4:
5: }
6:
7: @property (nonatomic, assign) MKMapView *mapView;
8:
9: @end
AnnotationView.m:1:
2: #import "AnnotationView.h"
3: #import "CurrentLocationAnnotation.h"
4:
5: @interface AnnotationView ()
6: @property (nonatomic, assign) BOOL hasBuiltInDraggingSupport;
7: @end
8:
9: @implementation AnnotationView
10: @synthesize hasBuiltInDraggingSupport;
11: @synthesize mapView;
12:
13: - (void)dealloc {
14: [super dealloc];
15: }
16:
17: - (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
18:
19: self.hasBuiltInDraggingSupport = [[MKAnnotationView class] instancesRespondToSelector:NSSelectorFromString(@"isDraggable")];
20:
21: if (self.hasBuiltInDraggingSupport) {
22: if ((self = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier])) {
23: [self performSelector:NSSelectorFromString(@"setDraggable:") withObject:[NSNumber numberWithBool:YES]];
24: }
25: }
26: self.canShowCallout = YES;
27:
28: return self;
29: }
30: @end
Step 6 - Now that we have our annotation view and placemark we can now add these to the map. I add the CurrentLocationAnnotation to the map in the locationManager:didUpdateToLocation:fromLocation method in the ProdjectNameViewController.m like so.1:
2: - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
3: {
4: [locationManager stopUpdatingLocation];
5: if(currentLocation == nil) currentLocation = newLocation;
6: else if (newLocation.horizontalAccuracy < currentLocation.horizontalAccuracy) currentLocation = newLocation;
7:
8: [myMapView setRegion:MKCoordinateRegionMake(currentLocation.coordinate, MKCoordinateSpanMake(0.05f, 0.05f))];
9: [myMapView setShowsUserLocation:NO];
10:
11: CurrentLocationAnnotation *annotation = [[[CurrentLocationAnnotation alloc] initWithCoordinate:self.currentLocation.coordinate addressDictionary:nil] autorelease];
12: annotation.title = @"Drag Me!";
13: annotation.subtitle = @"Drag pin to get desired location..";
14:
15: [myMapView addAnnotation:annotation];
16: }
Now as soon as the application is loaded and we call the [locationManager startUpdatingLocation] this method is called and we drop a pin on the users current location.Step 7 – Now we see where the AnnotationView class comes into play. In the Map View’s viewForAnnotation delegate method in the ProdjectNameViewController.m file we call for an AnnotationView which returns a draggable pin view.
1: - (MKAnnotationView *)mapView:(MKMapView *)MapView viewForAnnotation:(id <MKAnnotation>)annotation
2: {
3: static NSString * const kPinAnnotationIdentifier = @"PinIdentifier";
4: MKAnnotationView *draggablePinView = [MapView dequeueReusableAnnotationViewWithIdentifier:kPinAnnotationIdentifier];
5:
6: if (draggablePinView) {
7: draggablePinView.annotation = annotation;
8: } else {
9: draggablePinView = [[[AnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:kPinAnnotationIdentifier] autorelease];
10: if ([draggablePinView isKindOfClass:[AnnotationView class]]) {
11: ((AnnotationView *)draggablePinView).mapView = MapView;
12: }
13: }
14: return draggablePinView;
15: }
Step 8 – Once the users drags the pin, the didChangeDragState delegate method is called to update the latitude and longitude subtitle for the annotation.1: - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)annotationView didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState
2: {
3: if (oldState == MKAnnotationViewDragStateDragging) {
4: CurrentLocationAnnotation *annotation = (CurrentLocationAnnotation *)annotationView.annotation;
5: annotation.subtitle = [NSString stringWithFormat:@"%f %f", annotation.coordinate.latitude, annotation.coordinate.longitude];
6: }
7: }
Conclusion – This is a very simple implementation of the draggable annotations that iOS4 has released but I feel that this will give you a base to creating a much more functional application with draggable pins. If there are any questions please feel free to comment. Project source is attached. Happy coding!Source: iOS4DragDrop
lovely. Thanks
ReplyDelete