Finding Business Listings and Displaying with MapKit – Part 3

Time to wrap up this 3 part series and put the final polish on our map. We need to do two basic thing. We need to replace the native pin annotation views with our custom views and we need to center the map to the selected pin.

First, let’s take a look at our custom annotation view interface.

#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>

 

@interface BLPinAnnotation : MKAnnotationView {
    UIView *calloutView;
    UILabel *titleLabel;
    UILabel *subtitleLabel;
    UIImage *normalPin;
    UIImage *selectedPin;
}

@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *subtitleLabel;

-(void)setSelected:(BOOL)selected animated:(BOOL)animated;
-(CGPathRef)roundedRectPath:(CGRect)rect radius:(CGFloat)radius;

@end

There are a couple things to note here. First, a custom annotation view must extend the MKAnnotationView. Secondly, we need to override setSelected method and implement our custom functionality. Here’s the implementation:

#import "BLPinAnnotation.h"
#import "BLAnnotation.h"

 

@implementation BLPinAnnotation

@synthesize titleLabel, subtitleLabel;

- (id)initWithAnnotation:(id )annotation reuseIdentifier:(NSString *)reuseIdentifier
{  
    self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
   
    // Create selected and un-selected pin images
    normalPin = [UIImage imageNamed:@"map_pin.png"];
    selectedPin = [UIImage imageNamed:@"selected_map_pin.png"];
   
    // Set current pin to unselected image
    self.image = normalPin;
   
    // cast annotation an our expected BLAnnotation
   
    BLAnnotation *blAnnotation = (BLAnnotation *)annotation;
   
    // create title label and size to text
   
    titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, 280, 20)];
    titleLabel.text = blAnnotation.title;
    titleLabel.textAlignment = UITextAlignmentLeft;
    titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:13];
    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.7];
    titleLabel.shadowOffset = CGSizeMake(0, -1.0);
    titleLabel.backgroundColor = [UIColor clearColor];
    titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    titleLabel.numberOfLines = 3;

    CGSize titleLabelSize = [blAnnotation.title sizeWithFont: titleLabel.font constrainedToSize: CGSizeMake(280, 600) lineBreakMode: titleLabel.lineBreakMode];
    titleLabel.frame = CGRectMake(titleLabel.frame.origin.x, titleLabel.frame.origin.y, 280, titleLabelSize.height);

    // create subtitle label and size to text
   
    subtitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, titleLabel.frame.size.height + 8, 280, 20)];
    subtitleLabel.text = blAnnotation.subtitle;
    subtitleLabel.textAlignment = UITextAlignmentLeft;
    subtitleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:11];
    subtitleLabel.textColor = [UIColor whiteColor];
    subtitleLabel.shadowColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:.7];
    subtitleLabel.shadowOffset = CGSizeMake(0, -1.0);
    subtitleLabel.backgroundColor = [UIColor clearColor];
    subtitleLabel.lineBreakMode = UILineBreakModeWordWrap;
    subtitleLabel.numberOfLines = 5;
   
    CGSize subtitleLabelSize = [blAnnotation.subtitle sizeWithFont: subtitleLabel.font constrainedToSize: CGSizeMake(235, 600) lineBreakMode: subtitleLabel.lineBreakMode];
    subtitleLabel.frame = CGRectMake(subtitleLabel.frame.origin.x, subtitleLabel.frame.origin.y, subtitleLabelSize.width, subtitleLabelSize.height);
   
    // create callout view to be shown once a annotation view is selected
   
    calloutView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 300, titleLabel.frame.size.height + subtitleLabel.frame.size.height+13)];
    calloutView.hidden = YES;
    [self addSubview:calloutView];
   
    // create rounded rect background and size to text
   
    UIImageView *backgroundRect = [[UIImageView alloc] initWithFrame: CGRectMake(0, 0, calloutView.frame.size.width, calloutView.frame.size.height)];
    [backgroundRect.image drawInRect:calloutView.frame];
   
    UIGraphicsBeginImageContext(backgroundRect.frame.size);
     
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGRect frame = calloutView.bounds;
    [[UIColor colorWithRed:.2 green:.2 blue:.2 alpha:1] set];
    CGPathRef roundedRectPath = [self newPathForRoundedRect:frame radius:5];
    CGContextAddPath(ctx, roundedRectPath);
    CGContextFillPath(ctx);
    CGPathRelease(roundedRectPath);
   
    backgroundRect.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    [calloutView addSubview:backgroundRect];
    [calloutView addSubview:titleLabel];
    [calloutView addSubview:subtitleLabel];
   
    // position callout above pin
   
    calloutView.frame = CGRectMake(-140, -calloutView.frame.size.height+10, calloutView.frame.size.width, calloutView.frame.size.height);
   
    [backgroundRect release]; backgroundRect = nil;
   
    return self;
}

- (CGPathRef) roundedRectPath:(CGRect)rect radius:(CGFloat)radius
{
    // generate rounded rect path
   
    CGMutablePathRef retPath = CGPathCreateMutable();
   
    CGRect innerRect = CGRectInset(rect, radius, radius);
   
    CGFloat inside_right = innerRect.origin.x + innerRect.size.width;
    CGFloat outside_right = rect.origin.x + rect.size.width;
    CGFloat inside_bottom = innerRect.origin.y + innerRect.size.height;
    CGFloat outside_bottom = rect.origin.y + rect.size.height;
   
    CGFloat inside_top = innerRect.origin.y;
    CGFloat outside_top = rect.origin.y;
    CGFloat outside_left = rect.origin.x;
   
    CGPathMoveToPoint(retPath, NULL, innerRect.origin.x, outside_top);
   
    CGPathAddLineToPoint(retPath, NULL, inside_right, outside_top);
    CGPathAddArcToPoint(retPath, NULL, outside_right, outside_top, outside_right, inside_top, radius)
    CGPathAddLineToPoint(retPath, NULL, outside_right, inside_bottom);
    CGPathAddArcToPoint(retPath, NULL,  outside_right, outside_bottom, inside_right, outside_bottom, radius);
   
    CGPathAddLineToPoint(retPath, NULL, innerRect.origin.x, outside_bottom);
    CGPathAddArcToPoint(retPath, NULL,  outside_left, outside_bottom, outside_left, inside_bottom, radius);
    CGPathAddLineToPoint(retPath, NULL, outside_left, inside_top);
    CGPathAddArcToPoint(retPath, NULL,  outside_left, outside_top, innerRect.origin.x, outside_top, radius);
   
    CGPathCloseSubpath(retPath);
   
    return retPath;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    // show/hide callout and swap pin image
   
    calloutView.hidden = !selected;
   
    self.image = !selected ? normalPin : selectedPin;
   
    // dispatch an event to alert app a pin has been selected
   
    if(selected) [[NSNotificationCenter defaultCenter] postNotificationName:@"annotationSelected" object:self];
}

-(void) dealloc
{
    [super dealloc];
    [calloutView release]; calloutView = nil;
    [titleLabel release]; titleLabel = nil;
    [subtitleLabel release]; subtitleLabel = nil;
}

@end

The bulk of what is happening in the implementation is just constructing the call-out view. We create the call-out title, subtitle, and and background in the initWithAnnotation method. This method also accepts the annotation as one of the arguments which we use to populate the title and subtitle labels.

Next, we override the setSelected method. All we need to do here is swap the pin image and hide or show the call-out view.

Lastly, we dispatch an event to let the app know a pin has been selected. In the LocationsMap class you can find the selector method that responds to this event.

-(void) centerMapToAnnotation:(NSNotification *)note
{
    // Type dispatching annotation object
    BLPinAnnotation *annotation = (BLPinAnnotation *)[note object];
   
    // animate the map to the pin's coordinates
    [mapView setCenterCoordinate:annotation.annotation.coordinate animated: YES];
}

Finally, we just need to swap out our previous delegate method handling for viewForAnnotation to use or new PBLPinAnnotation instead of the native MKPinAnnotationView. Here’s the method:

- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
    // Specify which MKPinAnnotationView to use for each annotation.
   
    if (annotation != self.mapView.userLocation)
    {
        // Use custom annotation view for anything but the user's locaiton pin
       
        BLPinAnnotation *blAnnotationView = (BLPinAnnotation *)[self.mapView dequeueReusableAnnotationViewWithIdentifier: @"BLPin" ];
       
        if (blAnnotationView == nil)
        {
            blAnnotationView = [[BLPinAnnotation alloc] initWithAnnotation:annotation reuseIdentifier:@"BLPin"];
        }
        else
        {
            blAnnotationView.annotation = annotation;
            blAnnotationView.titleLabel.text = annotation.title;
            blAnnotationView.subtitleLabel.text = annotation.subtitle;
        }
       
        return blAnnotationView;
    }
    else
    {
        // Use a native MKAnnoationView for the user's location
       
        MKPinAnnotationView *businessListingPin = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier: @"BLUserPin" ];
       
        if (businessListingPin == nil) businessListingPin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"BLUserPin"];
       
        businessListingPin.canShowCallout = NO;
        businessListingPin.pinColor = MKPinAnnotationColorRed;
       
        return businessListingPin;
    }
}

So much like before, we are reusing our annotation views, only now we are using our custom annotation view for our business locations.

This concludes this three part series. I hope this series helps anyone looking for a straighforward and effective way to show a user business locations in an app. As always, the source is included below. If anyone has any improvements, suggestions, or requests, feel free to post a comment.

SOURCE FILES

http://www.zen-sign.com/finding-business-listings-and-displaying-with-mapkit-part-3/

posted @ 2010-10-22 14:59  周宏伟  阅读(406)  评论(0编辑  收藏  举报