I have created Custom Annotation with following:
-(MKAnnotationView*)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
MKPinAnnotationView *view = nil;
if (annotation != mapView.userLocation)
{
view = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"myAnnotationIdentifier"];
if (!view)
view = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"myAnnotationIdentifier"];
if (((CustomAnnotation *)annotation).annotationType == 1)
{
view.image = [UIImage imageNamed:#"type1.png"];
view.rightCalloutAccessoryView = nil;
view.canShowCallout = YES;
}
else
{
view.image = [UIImage imageNamed:#"type2.png"];
view.rightCalloutAccessoryView = nil;
view.canShowCallout = YES;
}
}
return view;
}
Problem: When User press and hold for 2 seconds on any Annotation Image (type1 or type2), Image gets replaced by Red PushPin(Default for iPhone MKPinAnnotationView).
I want to avoid this replacement. How can I do so?
Instead of declaring and creating an MKPinAnnotationView, declare and create a plain MKAnnotationView.
The MKPinAnnotationView likes to default to the red pin which is what it's for.
Use didDeselectAnnotationView and didSelectAnnotationView and reselect the image as you did by :-
view.image = [UIImage imageNamed:#"type2.png"];
Related
Hello Everyone i am new in developing app for I-Phone I have stuck in the Custom ANNotationView in mapKit. When i load map first Time it shows correctly Custom View , But after scrolling map it become the red colored PIN (default annotation in MapKit). Kindly Help me . Thank You in advance.
I am using the following code in viewForAnnotation delegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation: (id<MKAnnotation>)annotation
{
UIImageView *AvatarView=[[UIImageView alloc]initWithFrame:CGRectMake(0,0, 30, 30)];
AvatarView.layer.cornerRadius =AvatarView.frame.size.width / 2;
AvatarView.layer.masksToBounds = YES;
AvatarView.layer.borderColor =(__bridge CGColorRef)([UIColor colorWithRed:34.0/255.0 green:28.0/255.0 blue:36.0/255.0 alpha:1.0]) ;
AvatarView.layer.borderWidth = 1.0;
AvatarView.layer.zPosition=1;
MKPinAnnotationView* pinView =
(MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// Try to dequeue an existing pin view first.
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"CustomPinAnnotationView"];
// If an existing pin view was not available, create one.
//pinView.animatesDrop = YES;
annotationView.canShowCallout = YES;
if([[responseDataArray objectAtIndex:k] valueForKey:#"thumbPath"]!=nil && [[[responseDataArray objectAtIndex:k] valueForKey:#"thumbPath"] length]>0)
{
NSString *imgPath =[NSString stringWithFormat:#"%#%#",WebSiteUrl,[[responseDataArray objectAtIndex:k] valueForKey:#"thumbPath"]];
k++;
NSString* webStringURL = [imgPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL* url = [NSURL URLWithString:webStringURL];
NSString *imageName = [placeholderImageArray objectAtIndex:(arc4random() %placeholderImageArray.count)];
[AvatarView sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:imageName]];
}
[annotationView addSubview:AvatarView];
return annotationView;
}
else
{
k=0;
pinView.annotation = annotation;
}
return pinView;
}
I am using the following code to add Annotation
MKPointAnnotation *myAnnotation = [[MKPointAnnotation alloc] init];
myAnnotation.coordinate = coord;
myAnnotation.title = titleStr;
myAnnotation.subtitle = #"Best bar in Town";
[myLocation addAnnotation:myAnnotation];
It never goes to else part. I don't know what is the problem. Please help me to solve out this problem. A big thanks to You.
I am using a mutable array to render annotations on a map ,, the problem is when I tap on the disclosure button in any annotation in the map I can't get the title of that annotation because I don't know how to get its index and I would like to print it in mapLabel ... but I can't find the index of this annotation
and Here is the code:
-(MKAnnotationView *) mapView:(MKMapView *)mapMKMapView viewForAnnotation: (id<MKAnnotation>)annotation
{
MKPinAnnotationView *MyPin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
MyPin.pinColor = MKPinAnnotationColorPurple;
UIButton *adverButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[adverButton addTarget:self action:#selector(button:) forControlEvents:UIControlEventTouchUpInside];
MyPin.rightCalloutAccessoryView = adverButton;
MyPin.draggable = YES;
MyPin.highlighted = YES;
MyPin.animatesDrop = TRUE;
MyPin.canShowCallout = YES;
return MyPin;
}
-(void)button:(id)sender
{
//mapLabel.text = #"Here is the chosen ann title";
}
I am adding some annotations to a MKMapView and these annotations have a UIImageView, loaded asynchronously with AFNetworking, as left callout accessory view. Here's the implementation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *identifier = #"MapAnnotation";
if ([annotation isKindOfClass:[MapAnnotation class]]) {
MKAnnotationView *annotationView = (MKAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.image = [UIImage imageNamed:#"map_pin"];
UIImageView *userAvatar = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[userAvatar setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small",[[(MapAnnotation *)annotation user] facebookID]]] placeholderImage:[UIImage imageNamed:#"user_placeholder"]];
userAvatar.layer.cornerRadius = 4.0;
userAvatar.layer.masksToBounds = YES;
userAvatar.layer.borderColor = [[UIColor blackColor] CGColor];
userAvatar.layer.borderWidth = 1;
annotationView.leftCalloutAccessoryView = userAvatar;
UIButton *rightCallout = [UIButton buttonWithType:UIButtonTypeInfoLight];
annotationView.rightCalloutAccessoryView = rightCallout;
}
annotationView.annotation = annotation;
return annotationView;
}
return nil;
}
The problem here is: sometimes (not all the times) when annotations for different users are loaded into the map, they show the same image for all of them.
Let's say I have to load two annotations with this information:
Annotation 1
Name (title): Joshua
Datetime (subtitle): 19/06, 11:00
Image: www.myurl.com/joshua.jpg
Annotation 2
Name (title): Mathew
Datetime (subtitle): 10/06, 20:00
Image: www.myurl.com/mathew.jpg
Sometimes they are shown with title and subtitle correctly but the image loaded is the same for all of them (joshua's image or vice versa).
Am I missing something here is this method?
The call to dequeueReusableAnnotationViewWithIdentifier: will at the first call return nil which will satisfy annotationView == nil and set up your annotation view. So all the following calls to dequeueReusableAnnotationViewWithIdentifier: will return the first created annotation view with the assigned avatar image.
So [userAvatar setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small",[[(MapAnnotation *)annotation user] facebookID]]] placeholderImage:[UIImage imageNamed:#"user_placeholder"]]; is only called once.
You need to move annotation specific changes to the annotation view outside the "if nil" setup scope.
So you should instead set the userAvatar image each time the mapview ask for a annotationView.
So something like this:
MKAnnotationView *annotationView = (MKAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
annotationView.image = [UIImage imageNamed:#"map_pin"];
UIImageView *userAvatar = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
userAvatar.layer.cornerRadius = 4.0;
userAvatar.layer.masksToBounds = YES;
userAvatar.layer.borderColor = [[UIColor blackColor] CGColor];
userAvatar.layer.borderWidth = 1;
annotationView.leftCalloutAccessoryView = userAvatar;
UIButton *rightCallout = [UIButton buttonWithType:UIButtonTypeInfoLight];
annotationView.rightCalloutAccessoryView = rightCallout;
}
[((UIImageView *)annotationView.leftCalloutAccessoryView) setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small",[[(MapAnnotation *)annotation user] facebookID]]] placeholderImage:[UIImage imageNamed:#"user_placeholder"]];
This will assign the correct image each time the map view asks for an annotation view.
Cheers
Morten
This is my first question on this site.
My code is implemented in the following function using iOS 6 Mapkit, Objective-C.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
I have ONLY TWO annotations with custom pin images. One red pin and the other a orange pin. The red pin is closer to the user's current location within device map area and the other orange pin is 30 miles away (outside current map context). I am using different colors based on the data behind each pin.
Problem: Custom images for annotations are switching images.
Using all the tips on this site which includes usage of dequeueReusableAnnotationViewWithIdentifier, etc attached is my code.
On the very first display of the map on app launch, the error I see is that the orange pin displays on the device map and the red one is displayed outside. This is incorrect because the red image should have displayed on the device map.
If I tap the 'Find Me' button on the map to refresh my current location the red pin displays on the map which is now correct. When I tap 'Find Me' again, the red pin switches out to orange pin-- and it keeps switching or toggling pin image colors.
if (PinColor == 0) {
MKAnnotationView* pinview = (MKAnnotationView *)[self._mapView dequeueReusableAnnotationViewWithIdentifier:#"identifier"];
if (nil == pinview) {
MKAnnotationView* view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"identifier"];
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
view.opaque = YES;
view.image = [UIImage imageNamed:#"redpin.png"];
[view setCanShowCallout:YES];
if (self.newPinAdded) {
[view setSelected: YES];
}
return view;
}
return pinview;
}
else {
MKAnnotationView* pinview = (MKAnnotationView *)[self._mapView dequeueReusableAnnotationViewWithIdentifier:#"Orangeidentifier"];
if (nil == pinview) {
MKAnnotationView* view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"Orangeidentifier"];
view.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
view.image = [UIImage imageNamed:#"pin glabensmall.png"]; //orange pin
view.opaque = YES;
[view setCanShowCallout:YES];
if (self.newPinAdded) {
[view setSelected: YES];
}
return view;
}
return pinview;
}
I think your problem is a common one. Where is PinColor set? The mapview map ask for the annotationView to draw an annotation at any time for any annotation. If PinColor is set to 0 by some other method and then the mapview wants to draw an annotation any annotation it will draw a red pin. What you need to do is check which annotation you are drawing and then use the right colour for it. You can check the annotation by reading its title or if it is your own annotation class there may be some other property you can use.
SideNote: There are a few lines you've repeated for both pin version, you should have them outside the IF statement and cut out some lines. And you should use the mapView that viewForAnnotation give you:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation{
MKAnnotationView* pinview = nil;
if (annotation is the red one) {
pinview = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"identifier"];
if (nil == pinview) {
pinview = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"identifier"];
pinview.image = [UIImage imageNamed:#"redpin.png"];
}
} else {
pinview = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"Orangeidentifier"];
if (nil == pinview) {
pinview = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"Orangeidentifier"];
pinview.image = [UIImage imageNamed:#"pin glabensmall.png"]; //orange pin
}
}
[pinview setCanShowCallout:YES];
pinview.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinview.opaque = YES;
if (self.newPinAdded) {
[pinview setSelected: YES];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
//if it's user location, return nil
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
//try to dequeue an existing pin view first
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
pinView.pinColor = MKPinAnnotationColorRed;
//button on the right for popup for pins
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[rightButton addTarget:self
action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = rightButton;
//zoom button on the left of popup for pins
UIButton* leftButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
[leftButton setTitle:annotation.title forState:UIControlStateNormal];
[leftButton addTarget:self
action:#selector(zoomToLocation:) forControlEvents:UIControlEventTouchUpInside];
pinView.leftCalloutAccessoryView = leftButton;
return pinView;
}
//for map view annotation right button
-(void)showDetails:(id)sender{
NSLog(#"Annotation Click");
//fypAppDelegate *appDelegate = (fypAppDelegate *)[[UIApplication sharedApplication] delegate];
//Attraction *attraction = (Attraction *)[appDelegate.attractions objectAtIndex:sender];
infoViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:#"info"];
self.infoView = viewController;
[self.navigationController pushViewController:infoView animated:true];
}
//for map view annotation left button
-(void)zoomToLocation:(id)sender{
NSLog(#"Annotation Click");
}
Above is the delegate for the map annotations. I am able to show the pins and show the map annotation view but I don't know how to link the button events to the next view (infoViewController).
So as you guys can see, the right button is the one I want to use to enable user to view more information about that place while the left button, I want to allow user to zoom in into the coordinates of that pin.
The data are from the database I've created. Below is how I did it just for reference (in case you guys might need it)
-(void)putPins
{
fypAppDelegate *appDelegate = (fypAppDelegate *)[[UIApplication sharedApplication] delegate]; //get data
[appDelegate readTopAttractions];
int i = 0;
int count = appDelegate.attractions.count;
self.mapAnnotations = [[NSMutableArray alloc] initWithCapacity:appDelegate.attractions.count];
while (i < count) {
Attraction *attraction = (Attraction *)[appDelegate.attractions objectAtIndex:i];
i++;
//Set coordinates for pin
CLLocationCoordinate2D location;
location.latitude = (double)[[attraction xCoor] doubleValue];
location.longitude = (double)[[attraction yCoor] doubleValue];
MapPin *mapPin = [[MapPin alloc] init];
[mapPin setCoordinate:location];
[mapPin setName: [attraction name]];
NSString *desc = [attraction description];
int i = 0, position;
while(i < 50){
if ([desc characterAtIndex:i] == ' '){
position = i;
i++;
}
else
i++;
}
desc = [#"" stringByAppendingFormat:#"%#%#", [desc substringToIndex:position], #"..."];
[mapPin setDescription: desc];
[self.mapAnnotations addObject:mapPin];
}
[self.mapView addAnnotations:self.mapAnnotations];
}
please do tell me if you guys need more details.
Thank you! =)
In your showDetails: and zoomToLocation: methods, you can get a reference to the annotation whose callout button was tapped by doing the following:
MapPin *ann = (MapPin *)[mapView.selectedAnnotations objectAtIndex:0];
In zoomToLocation: you can then zoom in to that annotation using:
[mapView setRegion:
MKCoordinateRegionMakeWithDistance(ann.coordinate, 500, 500)
//500 meters vertical span, 500 meters horizontal span
animated:YES];
In showDetails:, you can pass ann or its properties to the detail view.
By the way, instead of calling custom methods using addTarget in viewForAnnotation, you can use the map view's calloutAccessoryControlTapped delegate method which gives more direct access to the annotation that was tapped. For example:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
MapPin *ann = (MapPin *)view.annotation;
if (control == view.rightCalloutAccessoryView)
{
NSLog(#"calloutAccessoryControlTapped: control=RIGHT");
//show detail view (or you can call your custom method here)...
}
else
if (control == view.leftCalloutAccessoryView)
{
NSLog(#"calloutAccessoryControlTapped: control=LEFT");
//zoom in (or you can call your custom method here)...
}
else
{
NSLog(#"calloutAccessoryControlTapped: unknown control");
}
}
Make sure you remove the addTarget calls from viewForAnnotation if you decide to use the calloutAccessoryControlTapped delegate method.
You want to zoom in to the particular pin? Is that right?
Therefore you can use the setRegion:animated: - method from MKMapView.
Example:
mapView = MKMapView
location = CLLLocationCoordinate2D
METERS_PER_MILE = 1609.344 (defined as a Constant)
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(location, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
MKCoordinateRegion adjustedRegion = [mapView regionThatFits:region];
[mapView setRegion:adjustedRegion animated:YES];
AppleDocs
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html
http://developer.apple.com/library/IOs/#documentation/MapKit/Reference/MapKitDataTypesReference/Reference/reference.html