I'm trying to add an image behind a MKPinAnnotationView. Seems like it should be rather easy to just do this in here:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
for (MKAnnotationView *aView in views)
[[aView superview] addSubview:imageView];
}
But the problem I am having in doing that is that the child subview to the pin will render on top of it not BEHIND it.
I have also tried:
for (MKAnnotationView *aView in views)
[[aView superview] insertSubview:imageView atIndex:1];
The problem with this is that, while it IS behind the pin, as soon as the map is repositioned, the image floats off the screen.
Any suggestions? Thanks
Thanks for the input, here's basically what I ended up doing without subclassing:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {
MKAnnotationView *annView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
UIImage *image = [UIImage imageNamed:#"image.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[annView addSubview:imageView];
[imageView release];
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
[annView addSubview:pinView];
[pinView release];
return annView;
}
I only needed one pin, so I set reuseIdentifier to nil.
I have subclassed the MKAnnotatonView and overrode the initWithAnnotation:resueIdentifier and drawRect methods to add another image behind the "front" image.
It seems that that is what Apple's MKAnnotationView class reference is suggesting us to do.
Only that it is tricky. If you are subclassing the MKAnnotationView, there is still the image property existing. They have done something about the properly so that it is linked with drawing. Perhaps, they have overridden the drawRect to draw the image AFTER the initialization is done.
Also, if you don't set the image property, the frame of your custom annotationView is set to size 0, and the drawRect method does not get called.
Finally, Apple says that the images should be totally filled, i.e. uses a transparent colour for the background of the drawings.
So: In your subclass:
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)identifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:identifier];
if (self)
{
UIButton *rightButton = [UIButton buttonWithType: UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:#selector(myShowAnnotationAddress:)
forControlEvents:UIControlEventTouchUpInside];
self.rightCalloutAccessoryView = rightButton;
self.canShowCallout = YES;
self.multipleTouchEnabled = NO;
self.frame = CGRectMake(0, 0, 65, 100);
self.backgroundColor = [UIColor clearColor];
return self;
}
return nil;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGPoint drawPoint = CGPointMake(0.0f, 0.0f);
UIImage *shadowImage = [UIImage imageNamed:#"shadow_image.png"];
[shadowImage drawAtPoint:drawPoint];
UIImage *frontImage = [UIImage imageNamed:#"front_image.png"];
[frontImage drawAtPoint:drawPoint];
CGContextRestoreGState(context);
}
After I made the answer, I realize that you actually wanted to draw behind MKPinAnnotationView. My answer is not that, although it shows where the drawing perhaps should be done. Obviously MKPinAnnottions has its own drawing method to present a Pin and its shadow.
I think that probably you can retrieve the Pin image from the self.image property. As to the shadow, I am not certain... It may be using OPEN GL drawing method to add the shadow, or simply combining a shadow image.
Finally, the image comes with animation. I am guessing that that is where the animation is run. At this stage, though, I have not tested.
Create a new composite annotationview that first adds your image and then the actual MKAnnotationView:
#implementation MyCustomAnnotationView
- (void) viewDidLoad
{
// First add the image to our subviews
UIImageView *view = [[UIImageView alloc] initWithImage: myImageProperty];
[self.view addSubview: view];
[view release];
// Then add the MKAnnotationView on top of it
MKAnnotationView *annotation = [[MKAnnotationView alloc] init];
[self.view addSubview: annotation];
[annotation release];
}
#end
Related
I have created a custom annotation view. In the setSelected method i have implemented a view as i need, now i want add a button to that view. But the button not detecting the click event,
it absorbed by the mapview. what should i do for adding button like rightcalloutaccessory.
Thanks in advance.
I solved this problem by changing control event for the UIButton as
[button addTarget:self action:#selector(clickEvent) forControlEvents:UIControlEventTouchDown]
Below is the part of my code to create custom annotations.
- (CustomAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[CustomAnnotation class]])
{
CustomAnnotationView *newAnnotationView = nil;
// determine the type of annotation, and produce the correct type of annotation view for it.
newAnnotationView = [[[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil delegate: targetController] autorelease];
[newAnnotationView setEnabled:YES];
[newAnnotationView setCanShowCallout:NO];
return newAnnotationView;
}
}
#interface CustomAnnotationView : MKAnnotationView
{
}
#end
#implementation CustomAnnotationView
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier delegate:(id)targetController
{
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
/* Set initial width as you want. ex: 50X50*/
self.frame = CGRectMake(0, 0, Initial_width, Initial_height);
self.backgroundColor = [UIColor redColor];
return self;
}
/* When you tap on an annotation this method is fired*/
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if(selected){
/* When you select annotation, instead of showing default callout you can increase the width &height of annotation and add your custom view here*/
self.frame = CGRectMake(0, 0, max_width, max_height);
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(6, 0, max_width-10, max_height)];
view.backgroundColor = [UIColor blackColor];
UIButton *but = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *img = [UIImage imageNamed:#"image.png"];
but = CGRectMake(x , y, img.size.width + 20, img.size.height + 15);
[but setImage:img forState:UIControlStateNormal];
[but addTarget:targetController action:#selector(methodName) forControlEvents:UIControlEventTouchDown];
[view addSubview:but];
[self addSubview:view];
}
else {
/* When you tap outside of annotation, set the initial frame for the annotation. and remove the custom view from annotation*/
self.frame = CGRectMake(0, 0, Initial_width, Initial_height);
[view removeFromSuperview];
}
}
I have code for drop pin. But i want to show title bar as in image above. How add images of star and review image?
use this delegate method
-(MKAnnotationView*)mapView:(MKMapView)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
// If you are showing the users location on the map you don't want to change it
MKAnnotationView *view = nil;
if (annotation != mapView.userLocation) {
// This is not the users location indicator (the blue dot)
view = [mapView dequeueReusableAnnotationViewWithIdentifier:#"myAnnotationIdentifier"];
if (!view) {
// Could not reuse a view ...
// Creating a new annotation view, in this case it still looks like a pin
view = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"myAnnotationIdentifier"] autorelease];
view.canShowCallOut = YES; // So that the callout can appear
UIImageView *myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"someName"]];
myImageView.frame = CGRectMake(0,0,31,31); // Change the size of the image to fit the callout
// Change this to rightCallout... to move the image to the right side
view.leftCalloutAccessoryView = myImageView;
[myImageView release], myImageView = nil;
}
}
return view;
}
It's the same as with custom cells in TableView.
You just need to create new subclass of MKAnnotationView, and draw the view what you want.
Otherwise, you can try this tutorial to get custom callouts: http://dev.tuyennguyen.ca/?p=298
To do so, you have to put images in the annotation to make it customized, i think this article http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1 by James Rantanen can help you out.
- (MKAnnotationView *)mapView:(MKMapView *)mapViewTmp viewForAnnotation:(id<MKAnnotation>)annotation
{
if(annotation == mapView.userLocation)
return nil;
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[mapViewTmp dequeueReusableAnnotationViewWithIdentifier:#"Pin"];
if (pinView ==nil) {
pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"Pin"];
{
if([addedAnns containsObject:annotation]){
MyAnnotation *a = [addedAnns objectAtIndex:[addedAnns indexOfObject:annotation]];
UIImage * image = [UIImage imageNamed:a.pinImage];
UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
imageView.frame=[pinView bounds];
[pinView addSubview:imageView];
//[imageView release];
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDeta`enter code here`ilDisclosure];
pinView.rightCalloutAccessoryView.tag = a.iAnnId;
[(UIButton *)pinView.rightCalloutAccessoryView addTarget:self action:#selector(openSpot:) forControlEvents:UIControlEventTouchUpInside];
pinView.enabled = YES;
pinView.centerOffset = CGPointMake(0,-15);
pinView.calloutOffset = CGPointMake(-8,0);
pinView.canShowCallout = YES;
}
}
pinView.animatesDrop = YES;
}
return pinView; // we cant release Or Auto release this object. Due To it will use futher
}
// The left accessory view to be used in the standard callout.
#property (retain, nonatomic) UIView *leftCalloutAccessoryView;
// The right accessory view to be used in the standard callout.
#property (retain, nonatomic) UIView *rightCalloutAccessoryView;
As you can see I am adding button to rightCalloutAccessoryView. Similarly you can Add Images To It.
You can use your custom MKAnnotationView.
How to make custom pin like foursquare friends map instead of pin on map?
image is here, http://www.socialmedianews.com.au/wp-content/uploads/2010/07/foursquare-friends-map.png
- (MKAnnotationView *)mapView:(MKMapView *)mv viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView) {
pinView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pinView"] autorelease];
pinView.image = [UIImage imageNamed:#"SPOON4.png"];
pinView.frame = CGRectMake(-30, 0, 70, 67.5);
//pinView.animatesDrop = YES; can't animate with custom pin images
pinView.canShowCallout = YES;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
} else {
pinView.annotation = annotation;
}
if (annotation == mapView.userLocation){
return nil; //default to blue dot
}
return pinView;
}
You need to use a custom MKAnnotationView.
You can use the MKAnnotationView class
as is or subclass it to provide custom
behavior as needed. The image property
of the class lets you set the
appearance of the annotation view
without subclassing directly. You
might also create custom subclasses as
a convenience and use them to put the
annotation view in a known state. For
example, the MKPinAnnotationView
subclass initializes the contents of
the annotation view to a pin image.
You want to replace the custom pin with a framed portrait. You can draw the frame with Quartz, or add the frame as a second transparent image. I would do the second adding the following to MKAnnotationView:
- (id)initWithAnnotation:(id <MKAnnotation>)annotation
reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
if (self != nil) {
self.opaque = NO;
self.frame = CGRectMake(0,0, self.portraitImage.size.width, self.portraitImage.size.height);
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self.frameImage drawInRect:CGRectMake(0, 0, frameImage.width, frameImage.height)];
[self.portraitImage drawInRect:CGRectMake(0, 0, portraitImage.width, portraitImage.height)];
}
I'm a bit lost here. I have a class subclassed from UIView called BalloonGuy. I initialize it like this.
- (id)initWithFrame:(CGRect)frame {
UIImage* loadedImage = [UIImage imageNamed:#"balloonguy.png"];
CGRect rect = CGRectMake(0, 0, loadedImage.size.width, loadedImage.size.height);
self = [super initWithFrame:rect];
image=[loadedImage retain];
self.opaque = YES;
self.backgroundColor = [UIColor clearColor];
return self;
}
I add it as a subview and everything is great.
What I can't do, however is change the image afterwards, ever. I won't list my failed attempts but what do I do?
I think you'll need to redraw your view when you change your image property, so overriding the property setter in this case may be a good idea:
- (void) setImage:(UIImage *) im {
[image release];
image = nil;
image = [im retain];
[self setNeedsLayout];
}
I've implemented a custom annotation derived from MKAnnotation called ContainerAnnotation and a custom annotation view derived from MKAnnotationView with a drawRect: method called ContainerAnnotationView. For some reason the drawRect: method is not getting called and I can't figure out why.
Here's the source code for my annotation view.
ContainerAnnotationView.h:
#interface ContainerAnnotationView : MKAnnotationView
{
}
#end
ContainerAnnotationView.m:
#implementation ContainerAnnotationView
- (void) drawRect: (CGRect) rect
{
// Draw the background image.
UIImage * backgroundImage = [UIImage imageNamed: #"container_flag_large.png"];
CGRect annotationRectangle = CGRectMake(0.0f, 0.0f, backgroundImage.size.width, backgroundImage.size.height);
[backgroundImage drawInRect: annotationRectangle];
// Draw the number of annotations.
[[UIColor whiteColor] set];
UIFont * font = [UIFont systemFontOfSize: [UIFont smallSystemFontSize]];
CGPoint point = CGPointMake(2, 1);
ContainerAnnotation * containerAnnotation = (ContainerAnnotation *) [self annotation];
NSString * text = [NSString stringWithFormat: #"%d", containerAnnotation.annotations.count];
[text drawAtPoint: point withFont: font];
}
#end
From my view controller:
- (MKAnnotationView *) mapView: (MKMapView *) mapView viewForAnnotation: (id <MKAnnotation>) annotation
{
if ([annotation isKindOfClass: [ContainerAnnotation class]])
{
ContainerAnnotationView * annotationView = (ContainerAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier: _containerMapAnnotationId];
if (annotationView == nil)
{
annotationView = [[[ContainerAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: _containerMapAnnotationId] autorelease];
annotationView.centerOffset = CGPointMake(0, -17.5);
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType: UIButtonTypeDetailDisclosure];
annotationView.canShowCallout = YES;
}
annotationView.annotation = annotation;
return annotationView;
}
// etc...
}
I have other annotations that use a vanilla MKAnnotation with an image that work fine. I also have another custom annotation view that doesn't implement drawRect: that works fine. Any idea what I'm doing wrong here?
The problem turned out to be that my drawRect: method was never being called because the frame was not set to a non-zero size. Adding an initWithAnnotation: method to do this solved the problem.
- (id) initWithAnnotation: (id <MKAnnotation>) annotation reuseIdentifier: (NSString *) reuseIdentifier
{
self = [super initWithAnnotation: annotation reuseIdentifier: reuseIdentifier];
if (self != nil)
{
self.frame = CGRectMake(0, 0, 30, 30);
self.opaque = NO;
}
return self;
}
Did you call setNeedsDisplay for this view subclass anywhere? (Just after you make this view visible is a good place.)