Search code examples
iosmkmapviewmapkitmkannotationmkannotationview

iOS Custom annotations for MKMapView


First of all I'm using kingpin in order to display an array of annotations. Everything works just as expected.

My question is: How can I create a custom view for an annotation? And I'm not referring to replacing the image of MKPinAnnotationView.

            if (annotationView == nil) {
            annotationView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:@"cluster"];
            annotationView.canShowCallout = YES;
            annotationView.image = [UIImage imageNamed:@"map_cluster"];
        }

With this I'm only able to replace the default pin with an Image. But this is not all I need.

http://imgur.com/nhUIvdx

I come from an Android background where I solved this problem by inflating a layout as a cluster (aka annotation in iOS). In this XML layout I'm positioning a container (the white frame), a picture and a counter. The result can bee seen in the picture below:

https://i.sstatic.net/dmbuL.jpg

How can I do this in iOS? Thanks in advance for any suggestions!


Solution

  • A quite late response, but I guess other people would still be interested

    In a MKAnnotationView subclass:

    First, define some dimensions:

    #define ckImageHeight 65
    #define ckImageWidth  55
    #define kBorder 5
    

    Then define the Annotation View's frame:

    self.frame = CGRectMake(0, 0, ckImageWidth, ckImageHeight);
    

    If you want the background to be an image and not just a color:

    self.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"map_checkin"]];
    

    Then make a placeholder for the image

    CGRect checkInPictureRect = CGRectMake(kBorder, kBorder, ckImageWidth - 9 , ckImageWidth - 9);
    UIView *checkInPictureView = [[UIView alloc]initWithFrame:checkInPictureRect];
    

    Then the fun starts:

    // Crop image
    UIImage *croppedImage = [ImageHelper centerCropImage:image];
    
    // Resize image
    CGSize checkInPictureSize = CGSizeMake(checkInPictureRect.size.width*1.5, checkInPictureRect.size.height*1.5);
    UIGraphicsBeginImageContext(checkInPictureSize);
    [croppedImage drawInRect:CGRectMake(0, 0, checkInPictureRect.size.width*1.5, checkInPictureRect.size.height*1.5)];
    UIImage* resizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    UIImageView *imageView = [[UIImageView alloc] initWithImage:resizedImage];
                                imageView.frame = checkInPictureView.bounds;
                                [checkInPictureView addSubview:imageView];
                                [self addSubview:checkInPictureView];
    
    // Counter
    UIView *counterView = [[UIView alloc]initWithFrame:CGRectMake(45, -2, 15, 15)];
    counterView.opaque = YES;
    (checkIn.isNow) ? [counterView setBackgroundColor:[UIColor enloopBlue]] : [counterView setBackgroundColor:[UIColor enloopGreen]];
    counterView.layer.cornerRadius = 8;
    
    self.counterLabel = [[UILabel alloc] init];
    self.counterLabel.frame = CGRectMake(4, 2, 10, 10);
    
    if (self.count >= 10) {
        counterView.frame = CGRectMake(45, -2, 18, 18);
        self.counterLabel.frame = CGRectMake(3, 3, 12, 12);
    }
    
    [self.counterLabel setTextColor:[UIColor whiteColor]];
    [self.counterLabel setFont:[UIFont fontWithName: @"Trebuchet MS" size: 11.0f]];
    [self.counterLabel setText:[[NSString alloc] initWithFormat:@"%lu", (unsigned long)self.count]];
    [counterView addSubview:self.counterLabel];
    [counterView bringSubviewToFront:self.counterLabel];
    [self addSubview:counterView];
    

    As for the centerCropImage helper, nothing special:

    + (UIImage *)centerCropImage:(UIImage *)image {
        // Use smallest side length as crop square length
        CGFloat squareLength = MIN(image.size.width, image.size.height);
        // Center the crop area
        CGRect clippedRect = CGRectMake((image.size.width - squareLength) / 2, (image.size.height - squareLength) / 2, squareLength, squareLength);
    
        CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], clippedRect);
        UIImage * croppedImage = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        return croppedImage;
    }
    

    I know there are quite a few things to improve, but untill then I hope it will help others. :)