I have a map view with pins that when the user selects a pin it goes to a detail screen for that pin. I also have a table view that when the user selects an item it goes to the same type detail view.
Here's the problem ...
It seems to work fine in everything from 3.1.3 to 4.1. That is the detail view matches with the pin. But I have a user who just upgraded to iOS 4.2 and says that under 4.1 it worked fine, but in 4.2 the pin selection takes him to the detail for a different pin. But in the table view it still works fine.
Is it possible that there was a change in MKMapView in iOS 4.2 that changes how the pinID is selected?
Addition - I have added the relevant parts of viewForAnnotation and checkButtonTapped
- (MKPinAnnotationView *)mapView:(MKMapView *)eMapView viewForAnnotation:(id <MKAnnotation>)annotation {
int postTag = 0;
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[eMapView dequeueReusableAnnotationViewWithIdentifier:#"Pin"];
if(pinView == nil) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"Pin"];
pinView.frame = CGRectMake(0, 0, 25, 25);
} else {
pinView.annotation = annotation;
}
// Set up the Right callout
UIButton *myDetailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
myDetailButton.frame = CGRectMake(0, 0, 23, 23);
myDetailButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myDetailButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
[myDetailButton addTarget:self action:#selector(checkButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
// Identify which pin is being selected
if ([[annotation title] isEqualToString:#"Current Location"]) {
postTag = 99999;
} else {
postTag = [annotation getPinID];
}
myDetailButton.tag = postTag;
pinView.rightCalloutAccessoryView = myDetailButton;
pinView.animatesDrop = YES;
// Set to show a callout on the pin
pinView.canShowCallout = YES;
return pinView;
}
// Method to show detail view when the callOut button is selected
- (IBAction) checkButtonTapped: (id) sender {
int nrButtonPressed = ((UIButton *)sender).tag;
if (nrButtonPressed < 99999) {
if (self.showDetailView == nil) {
DetailData *tmpViewController = [[DetailData alloc] initWithNibName:#"Detail" bundle:nil];
self.showDetailView = tmpViewController;
[tmpViewController release];
}
buttonDetail = [[mapView annotations] objectAtIndex:(nrButtonPressed-1)];
NSMutableArray *tmpArray = [NSMutableArray arrayWithObjects: [buttonDetail getDataI], [buttonDetail getDataII], [buttonDetail getDataIII], [buttonDetail getDataIV], [buttonDetail getDataV], nil];
self.showDetailView.eventData = [[NSMutableArray alloc] initWithArray:tmpArray copyItems:YES];
[self.navigationController pushViewController:self.showDetailView animated:YES];
[self.showDetailView.eventData release];
}
}
As you can see, in checkButtonTapped I record the pin selected then collect the data from the annotation for that pin. The problem seems to be that nrButtonPressed is now incorrect. But it is fine when compiled in 4.1
In checkButtonTapped, the annotation is being identified using the button's tag. The button's tag is set in viewForAnnotation by calling the custom method getPinID.
The button tag is used as the index into the mapView annotations array. It's not clear how the getPinID method figures out what index it is at in the mapView's annotations array. I'm not sure it's wise to assume an annotation is going to be at a specific index in the array. Even if mapView doesn't shuffle the annotations around, your app might be constantly adding and removing annotations.
Because you are assigning the button as a callout accessory in the annotation view, rather than using a button tag to identify the annotation, you can use the mapView's own mapView:annotationView:calloutAccessoryControlTapped: delegate method.
Instead of doing addTarget on the button and having it call your custom method, remove the call to addTarget and implement calloutAccessoryControlTapped.
In calloutAccessoryControlTapped, you can directly access the annotation using view.annotation. You can also easily check if the annotation is the "user location":
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if (view.annotation == mapView.userLocation)
return;
buttonDetail = (MyCustomAnnotationClass *)view.annotation;
//show detail view using buttonDetail...
}
Additionally, in viewForAnnotation, you are creating a button every time even if the view is being re-used. So a re-used annotation view probably ends up getting multiple buttons on top of each other. The button should only be created and set in the if(pinView == nil) section.
If anyone would care to see what I am using now (and it is working) ...
- (MKPinAnnotationView *)mapView:(MKMapView *)eMapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[eMapView dequeueReusableAnnotationViewWithIdentifier:#"Pin"];
if(pinView == nil) {
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"Pin"];
pinView.frame = CGRectMake(0, 0, 25, 25);
} else {
pinView.annotation = annotation;
}
// Set up the Right callout
UIButton *myDetailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
myDetailButton.frame = CGRectMake(0, 0, 23, 23);
myDetailButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
myDetailButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
pinView.rightCalloutAccessoryView = myDetailButton;
pinView.animatesDrop = YES;
// Set to show a callout on the pin
pinView.canShowCallout = YES;
return pinView;
}
// Added this at the suggestion of aBitObvious on StackOverflow - 120810
- (void)mapView:(MKMapView *)eMapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
if (view.annotation == eMapView.userLocation)
return;
if (self.showDetailView == nil) {
DetailData *tmpViewController = [[DetailData alloc] initWithNibName:#"DetailData" bundle:nil];
self.showDetailView = tmpViewController;
[tmpViewController release];
}
buttonDetail = (MyCustomAnnotationClass *)view.annotation;
NSMutableArray *tmpArray = [NSMutableArray arrayWithObjects: [buttonDetail getDataI], [buttonDetail getDataII], [buttonDetail getDataIII], [buttonDetail getDataIV], [buttonDetail getDataV], nil];
self.showDetailView.eventData = [[NSMutableArray alloc] initWithArray:tmpArray copyItems:YES];
[self.navigationController pushViewController:self.showDetailView animated:YES];
[self.showDetailView.eventData release];
}
Thanks again!
Related
I have a table view with an ordered list of objects, ordered by distance between me and the object. I need to show annotations in a map to show to the user the position of the objects but i need to show in the same order than table´s order because then i need to get with one has been pressed to show info of each object.
The problem is that when I put the annotations in the map each time I put them the tag number of the annotation is different. Is like the map added the annotations in a random order.
I have an annotations array and then I add that array to the map:
MKPointAnnotation* tallerAnnotation = [[MKPointAnnotation alloc] init];
[tallerAnnotation setCoordinate:CLLocationCoordinate2DMake([taller getLatitudTaller], [taller getLongitudTaller])];
[tallerAnnotation setTitle:[taller getNombre]];
[tallerAnnotation setSubtitle:[taller getDomicilio]];
[annotations addObject:tallerAnnotation];
And then add the array to the map:
[mapa addAnnotations:annotations];
Here is the code where I custom the annotation:
- (MKAnnotationView *) mapView: (MKMapView *) mapa viewForAnnotation: (id <MKAnnotation>) annotation_ {
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.mapa dequeueReusableAnnotationViewWithIdentifier: #"YourPinId"];
if (pin == nil) {
pin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation_ reuseIdentifier: #"YourPinId"];
}
else {
pin.annotation = annotation_;
}
if ([[annotation_ title]isEqualToString:#"Tu posición"])
pin.pinColor = MKPinAnnotationColorRed;
else
{
[pin setCanShowCallout:YES];
pin.pinColor = MKPinAnnotationColorGreen;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(getInfoTalleres:) forControlEvents:UIControlEventTouchUpInside];
[rightButton setTag:posicionActualTaller];
pin.rightCalloutAccessoryView = rightButton;
UILabel *l1=[[UILabel alloc] init];
l1.frame=CGRectMake(0, 5, 50, 15);
l1.backgroundColor = [UIColor clearColor];
l1.textColor = [UIColor whiteColor];
l1.font=[UIFont fontWithName:#"Arial Rounded MT Bold" size:(10.0)];
l1.text=[[[mainDelegate getTalleres]objectAtIndex: posicionActualTaller]getDistancia];
posicionActualTaller +=1;
UIView *leftCAV = [[UIView alloc] initWithFrame:CGRectMake(0,0,50,15)];
[leftCAV addSubview : l1];
pin.leftCalloutAccessoryView = leftCAV;
}
pin.animatesDrop = YES;
return pin;
}
I would like to do something like this:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"Tag del taller = %i",[control tag]);
[mainDelegate setTallerActual:[[mainDelegate getTalleres]objectAtIndex:[control tag]]];
[self detalleTaller];
}
[control tag] has differents values each time I enter into map view so that way it´s impossible do something like [objects objectAtIndex:[control tag]];
Please I need help.
Thanks in advance.
Ok finally I have found a solution.
When I create the
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(getInfoTalleres:) forControlEvents:UIControlEventTouchUpInside];
for the annotation I assign the annotation title to the button this way:
[rightButton setTitle:[annotation_ subtitle] forState:UIControlStateNormal];
Then:
-(void)getInfoTalleres:(UIButton* )sender
{
for (int i = 0; i < [[mainDelegate getTalleres]count]; i++)
{
if ([[sender currentTitle] isEqualToString:[[[mainDelegate getTalleres]objectAtIndex:i]getDomicilio]])
{
[mainDelegate setTallerActual:[[mainDelegate getTalleres]objectAtIndex:i]];
[self detalleTaller];
break;
}
}
}
Hope somebody can use this solution.
Thanks a lot Cocoa.
Regards.
Check the Answer here it access title property Determine which MKPinAnnotationView was selected? and the tutorial http://www.scribd.com/doc/91629556/192/The-MKAnnotationView-class
let me know if you still not find answer
I have a map view and there are 10 store locations which data comes via webservice. I just want to push to my detail view to show address, telephone and other informations of the clicked store.
I need to pass data to my detailview when user tapped or touch up inside to a annotation on mapkit. There are 10 annotations in my mapview and first I want to know, how can I understand or how can I get the annotationID of which annotation is clicked?
this is the method I return pins
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]) return nil;
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.pinColor=MKPinAnnotationColorPurple;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[rightButton addTarget:self
action:#selector(showDetails:)
forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
/* and my action method for clicked or tapped annotation: */
- (IBAction)showDetails:(id)sender{
NSLog(#"Annotation Click");
[[mtMap selectedAnnotations]objectAtIndex:0];
magazaDetayViewController *detail = [[magazaDetayViewController
alloc]initWithNibName:#"magazaDetayViewController" bundle:nil];
detail.sehir=#"";
detail.magazaAdi=#"";
detail.adres=#"";
detail.telefon=#"";
detail.fax=#"";
[self.navigationController pushViewController:detail animated:YES];
}
if i can just get the clicked annotation index no i can fill detail properties with my array.
if this is impossible is there any other way to do it?
First in your annotaion view delegat make a button to go in detail view like bellow:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *mypin = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"current"];
mypin.pinColor = MKPinAnnotationColorPurple;
mypin.backgroundColor = [UIColor clearColor];
UIButton *goToDetail = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
mypin.rightCalloutAccessoryView = myBtn;
mypin.draggable = NO;
mypin.highlighted = YES;
mypin.animatesDrop = TRUE;
mypin.canShowCallout = YES;
return mypin;
}
Now use the following delegate whenever the button in annotationView will get tapped the following delegate will be called from where you can easly get which particular annotaion's button is tapped
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
annotation *annView = view.annotation;
detailedViewOfList *detailView = [[detailedViewOfList alloc]init];
detailView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
detailView.address = annView.address;
detailView.phoneNumber = annView.phonenumber;
[self presentModalViewController:detailView animated:YES];
}
here annotaion is a class importing MKAnnotaion.h and address and phonenumber are properties of annotaion class you can make many more while the address and phoneNumber properties of detailView class are strong. So that you can pass values. Hope this will help you!
Bellow is Delegate method of MapView......
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
}
when user tap annotation pin above method call automaticaly if method define in .m file nut first declare MKMapViewDelegate delegate in .h file
and also you can get title or subtitle and id from google place API....
link is...
https://developers.google.com/maps/documentation/places/
for(int i=0; i<[arrLat count];i++) {
CLLocationCoordinate2D theCoordinate1;
konumpin* myAnnotation1=[[konumpin alloc] init];
theCoordinate1.latitude = [[arrLong objectAtIndex:i] doubleValue];
theCoordinate1.longitude = [[arrLat objectAtIndex:i] doubleValue];
myAnnotation1.coordinate=theCoordinate1;
myAnnotation1.title=[arrMagaza objectAtIndex:i];
[annotations addObject:myAnnotation1];
}
[mtMap addAnnotations:annotations];
Hi can some1 plz help me....
The problem is this: Everything seems alright.. i am able to push to another page once i tap on the detailButton. However, the page which i am able to push to is always only a view without any values passed into the labels. All my labels in the view have nothing inside.. Am i doing something wrong or any wrong concept here?
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
BOOL isRed = NO;
//BOOL isPurple = YES;
if([annotation isKindOfClass:[MyAnnotation2 class]])
isRed = YES;
MKPinAnnotationView *pinView = nil;
if(isRed) {
static NSString *redPin = #"redPin";
pinView = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:redPin];
if (!pinView) {
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:redPin] autorelease];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = detailButton;
}
else{
pinView.annotation = annotation;
}
}
else {
static NSString *greenPin = #"greenPin";
pinView = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:greenPin];
if (!pinView) {
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:greenPin] autorelease];
pinView.pinColor = MKPinAnnotationColorGreen;
pinView.animatesDrop = NO;
pinView.canShowCallout = YES;
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = detailButton;
}
}
return pinView;
}
//
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"calloutAccessoryControlTapped");
if (self.patientDetailsViewController == nil) {
self.patientDetailsViewController = [[[PatientDetailsViewController alloc] initWithNibName:#"PatientDetailsViewController" bundle:nil] autorelease];
}
MyAnnotation2 *selectedObject = (MyAnnotation2 *)view.annotation;
_patientDetailsViewController.nric = selectedObject.title;
NSLog(#"%#",_patientDetailsViewController.nric);
[self.navigationController pushViewController:_patientDetailsViewController animated:YES];
}
Don't use your own target/action and tagging for the callout accessory button.
Instead, use the MKMapViewDelegate method calloutAccessoryControlTapped:. Remove the addTarget line and implement the delegate method.
In that delegate method, you can access the annotation tapped using:
MyAnnotation2 *selectedObject = (MyAnnotation2 *)view.annotation;
I have declared the following in my .h file:
Annotation *annoForMoreDetails.
However when I try to set it to the current annotation in the method
- (MKAnnotationView *)mapView:(MKMapView *)mapViews viewForAnnotation:(id <MKAnnotation> )annotation
It gets set to the wrong object.
Here is my code:
- (MKAnnotationView *)mapView:(MKMapView *)mapViews viewForAnnotation:(id <MKAnnotation> )annotation
{
NSLog(#"welcome into the map view annotation");
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
for (annotation in [mapView annotations]) {
if ([[mapView annotations] containsObject:annotation]) {
annoForMoreDetails = annotation;
}
}
if (annoForMoreDetails.coordinate.latitude != mapViews.userLocation.coordinate.latitude && annoForMoreDetails.coordinate.longitude != mapViews.userLocation.coordinate.longitude) {
// try to dequeue an existing pin view first
static NSString* AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView* pinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
pinView.pinColor = MKPinAnnotationColorGreen;
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton setTitle:annotation.title forState:UIControlStateNormal];
[rightButton addTarget:self
action:#selector(moreDetails)
forControlEvents:UIControlEventTouchUpInside];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
else if (annoForMoreDetails.coordinate.latitude == mapViews.userLocation.coordinate.latitude && annoForMoreDetails.coordinate.longitude == mapViews.userLocation.coordinate.longitude) {
return nil;
}
return nil;
}
- (void)moreDetails {
annotationCalloutView.frame = CGRectMake(70, 120, 188, 218);
annotationCalloutView.alpha = 0.0;
mapView.userInteractionEnabled = NO;
annotationCalloutView.userInteractionEnabled = YES;
titleLabel.text = annoForMoreDetails.title;
titleLabel.numberOfLines = 0;
titleLabel.userInteractionEnabled = NO;
addressLabel.text = annoForMoreDetails.subtitle;
[self.view addSubview:annotationCalloutView];
[UIView beginAnimations:#"callout" context:NULL];
[UIView setAnimationDuration:0.6];
annotationCalloutView.alpha = 1.0;
[UIView commitAnimations];
}
If you see anything wrong please point it out!
Are you trying to use annoForMoreDetails to know which annotation the user tapped on so you can show more details? If that's the case, there's an easier way. Use the map view's calloutAccessoryControlTapped delegate method. It will be called when the user taps on the accessory button and it will pass the annotation view which includes the annotation as a property.
Remove the annoForMoreDetails instance variable and in viewForAnnotation, remove the for-loop that sets annoForMoreDetails (I think it ends up setting it to the last annotation every time). Remove all other references to annoForMoreDetails.
Also in viewForAnnotation, remove the addTarget line on the rightButton since you'll replace your custom moreDetails method with an implementation of calloutAccessoryControlTapped:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
Annotation *annoForMoreDetails = (Annotation *)view.annotation;
//the code currently in moreDetails method goes here...
}
In my application i want to display custom view(with buttons) in the case of user touches annotation on map(standard pin).
i know that i can subclass MKAnnotationView and change the view of pin. but how can i change the look of Additional information view, that appears when user touches pin(by default it displays title and subtitle of MKAnnotation object).
how can i do this?
Thanks.
- (MKAnnotationView *)mapView:(MKMapView *)mV viewForAnnotation:(id )annotation
{
MKPinAnnotationView *pinView = nil;
static NSString *defaultPinID = #"ReusedPin";
pinView = (MKPinAnnotationView*)[mVdequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil )
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];
if (((PinAnnotationView*)annotation).tag == 0 )
{
pinView.pinColor = MKPinAnnotationColorPurple;
}
else {
pinView.pinColor = MKPinAnnotationColorRed;
}
pinView.canShowCallout = YES;
pinView.animatesDrop = YES;
UIImageView *pinImageView = [[UIImageView alloc] initWithFrame:CGRectMake(-5, 0, 34, 34)];
UIImage *pinImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"icon" ofType:#"png"]];
pinImageView.image = pinImage;
[pinImage release];
[pinView addSubview:pinImageView];
[pinImageView release];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
btn.tag = ((PinAnnotationView*)annotation).tag;
pinView.rightCalloutAccessoryView = btn;
return pinView;
}
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
if ( control.tag !=0) {
ShowProviderDetailVC *viewControlle = [[ShowProviderDetailVC alloc]initWithNibName:#"ShowProviderDetailVC" bundle:nil];
viewControlle.lastViewName = #"SearchView";
for (NSMutableDictionary* dict in globalLocArray) {
if ( control.tag ==[[dict valueForKey:#"ID"] intValue] )
{
viewControlle.providerInfoDict = dict;
}
}
[self.navigationController pushViewController:viewControlle animated:YES];
[viewControlle release];
}
Custom callout bubble can be displayed by making sub class of UIView Class.
You can check these links.
http://blog.asynchrony.com/2010/09/building-custom-map-annotation-callouts-part-1/
http://blog.asynchrony.com/2010/09/building-custom-map-annotation-callouts-part-2/