Nirav Bhatt
Nirav Bhatt

Reputation: 6969

iOS - How to use mkmapview as location picker

In my app I have two address UITextfields to be filled up using address text. I want this behavior:

I believe this is a very straight use case of mkmapview. But can't find any examples of this already done. Am I missing something pretty obvious in UIKit?

Many thanks for your help....

EDIT:

I read up Apple's docs, and I found many solutions that point towards this. However all of them seems to assume that MKPinAnnotationView (or MKAnnotationView) is overridden.

Is it necessary? If yes, how much I need to provide in the subclass apart from dragging?

Upvotes: 2

Views: 3833

Answers (1)

Rob
Rob

Reputation: 438232

No need to subclass MKPinAnnotationView. Just use it. You only should be subclass it if you're looking for some custom behavior. But it is useful to write a viewForAnnotation so you can configure it properly. But typically I find the configuration of a standard MKPinAnnotationView to be simple enough that no subclassing is needed:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;

    MKPinAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"DroppedPin"];

    annotationView.draggable = YES;
    annotationView.canShowCallout = YES;
    annotationView.animatesDrop = YES;

    return annotationView;
}

Having said that, it is not uncommon to have your own annotation class. I might do this for at least two reasons:

  1. You might keep the reverse geocoded MKPlacemark as a property of your annotation. Logically, the geocoded information seems like a property of the annotation, not of the view. You can then inquire this placemark property of the dropped pin to get whatever information you need to pass back to your other view.

  2. If you want, you could configure your annotation to both perform the reverse geocode lookup which would the placemark property, but also change the title to the reverse geocoded address when you change its coordinate, too. This way, the user is getting active feedback about what the reverse geocoding as they're dragging and dropping the pin on the map, but the code is still super simple:

Thus, you might have an annotation class like:

@interface DroppedAnnotation : NSObject <MKAnnotation>

@property (nonatomic, strong) MKPlacemark *placemark;
@property (nonatomic) CLLocationCoordinate2D coordinate;
@property (nonatomic, strong) NSString *title;
@property (nonatomic, strong) NSString *subtitle;

@end

@implementation DroppedAnnotation

- (void)setCoordinate:(CLLocationCoordinate2D)coordinate
{
    CLLocation *location = [[CLLocation alloc] initWithLatitude:coordinate.latitude
                                                      longitude:coordinate.longitude];
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
        // do whatever you want here ... I'm just grabbing the first placemark

        if ([placemarks count] > 0 && error == nil)
        {
            self.placemark = placemarks[0];

            NSArray *formattedAddressLines = self.placemark.addressDictionary[@"FormattedAddressLines"];
            self.title = [formattedAddressLines componentsJoinedByString:@", "];
        }
    }];

    _coordinate = coordinate;
}

@end

And your view controller can use this new class:

@property (nonatomic, weak) id<MKAnnotation> droppedAnnotation;

- (void)dropPin
{
    // if we've already dropped a pin, remove it

    if (self.droppedAnnotation)
        [self.mapView removeAnnotation:self.droppedAnnotation];

    // create new dropped pin

    DroppedAnnotation *annotation = [[DroppedAnnotation alloc] init];
    annotation.coordinate = self.mapView.centerCoordinate;
    annotation.title = @"Dropped pin"; // initialize the title
    [self.mapView addAnnotation:annotation];

    self.droppedAnnotation = annotation;
}

To be clear, you don't need your own annotation class. You could use the standard MKPointAnnotation for example. But then your view controller has to keep call and keep track of reverse geocoded information itself. I just think the code is a little cleaner and more logical when you use a custom annotation class.

Upvotes: 7

Related Questions