I have a map with a disclosure button on the callout of it's annotations, which when clicked displays a detail view. Each point on the map needs to load up different data in the detail view - I dont want to build separate views for each annotation, for obvious reasons, but I am unsure of the best way to do this.
How do I flag which annotation has been clicked, so I can load the correct data through the detail's view controller?
You can implement the data loading function in your annotation class. To do that you will need to have a reference to that data in your annotation class.
Your annotation class can be something like
#interface MyAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
MyDataObject *mydataObject;
}
- (id) initWithCoordinate:(CLLocoationCoordinate2D)coord withMyData:(MyDataObject*)aDataObject;
- (void) loadDetailView; // this function will load the data in myDataObject to the DetailView
To get the callout touch, you can implement calloutAccessaryControlTapped function in the controller where you implement MKMapViewDelegate.
The function will be something like
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
MyAnnotation *myAnnotation = (MyAnnotation*)view.annotation;
[myAnnotation loadDetailView];
}
Edit
The implementation of initWithCoordinate will be something like:
#implementation MyAnnotation
....
- (id) initWithCoordinate:(CLLocoationCoordinate2D)coord withMyData:(MyDataObject*)aDataObject{
coordinate = coord; // cannot use "self.coordinate" here as CLLocationCoordinate2D is double instead of object (?)
self.title = <# some name #> // assign some string to the title property
self.mydataObject = aDataObject; // this is your data object
return self;
}
....
Related
How do I keep data associated with an MKAnnotation object after the user taps the pin, sees a callout, and taps the disclosure button which opens a detailed view controller? I want to display all data associated with the pin in the detail view controller.
I have a simple MKAnnotation class that looks like:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface VoiceMemoryAnnotation : NSObject <MKAnnotation> {
NSString * blobkey;
}
#property (nonatomic, retain) NSString * blobkey;
-(id)initWithBlobkey:(NSString *) key andCoordinate:(CLLocationCoordinate2D) c;
#end
I implemented the call back "viewForAnnotation"
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView*singleAnnotationView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:nil];
// PM: this pin will have a callout (i.e. dont' forget to override title function! Else exception thrown)
singleAnnotationView.canShowCallout = YES;
// PM: add disclosure button
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
// PM: when user taps disclosure button, bring them to another page with details about the selected voice memory
[rightButton addTarget:self action:#selector(showPinDetails:) forControlEvents:UIControlEventTouchUpInside];
singleAnnotationView.rightCalloutAccessoryView = rightButton;
return singleAnnotationView;
}
If I understand correctly, the above method is called when you add a VoiceMemoryAnnotation to a map object. When this viewForAnnotation is called, I simply allocate a MKPinAnnotationView object and return it. When the user taps this retuned pin, they see the callout. As soon as they click the disclosure button it calls "showPinDetails":
- (void)showPinDetails:(id)sender
{
detailViewController = [[MemoryDetailViewController alloc]initWithNibName:#"MemoryDetailViewController" bundle:nil];
[self presentModalViewController:detailViewController animated:YES];
}
The problem is the "sender" object does not contain any information about which pin was selected. Is there some way I can pass in the selected annotation to the showPinDetails method?
In the showPinDetails: method, you can get the currently selected annotation from the map view's selectedAnnotations property.
That property is an NSArray but since the map view only allows one annotation to be selected at a time, you would just use the object at index 0. For example:
- (void)showPinDetails:(id)sender
{
if (mapView.selectedAnnotations.count == 0)
{
//no annotation is currently selected
return;
}
id<MKAnnotation> selectedAnn = [mapView.selectedAnnotations objectAtIndex:0];
if ([selectedAnn isKindOfClass[VoiceMemoryAnnotation class]])
{
VoiceMemoryAnnotation *vma = (VoiceMemoryAnnotation *)selectedAnn;
NSLog(#"selected VMA = %#, blobkey=%#", vma, vma.blobkey);
}
else
{
NSLog(#"selected annotation (not a VMA) = %#", selectedAnn);
}
detailViewController = [[MemoryDetailViewController alloc]initWithNibName:#"MemoryDetailViewController" bundle:nil];
[self presentModalViewController:detailViewController animated:YES];
}
Instead of using a custom button action method, it can be easier to use the map view's calloutAccessoryControlTapped delegate method which lets you get access to the selected annotation more directly. In viewForAnnotation, remove the addTarget and just implement the delegate method:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
id<MKAnnotation> selectedAnn = view.annotation;
if ([selectedAnn isKindOfClass[VoiceMemoryAnnotation class]])
{
VoiceMemoryAnnotation *vma = (VoiceMemoryAnnotation *)selectedAnn;
NSLog(#"selected VMA = %#, blobkey=%#", vma, vma.blobkey);
}
else
{
NSLog(#"selected annotation (not a VMA) = %#", selectedAnn);
}
//do something with the selected annotation...
}
I have an object that implements the MKAnnotation protocol:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface VoiceMemoryAnnotation : NSObject <MKAnnotation> {
NSString * blobkey;
}
#property (nonatomic, retain) NSString * blobkey;
-(id)initWithBlobkey:(NSString *) key andCoordinate:(CLLocationCoordinate2D) c;
#end
Adding this object a map works perfectly since I can see the red pins being dropped. However, the problem arises when I want to set this object to show a callout.
I cannot do annotation.showCallOut=YES because an "MkAnnotation" does not have this property, but a MkAnnotationView does. How do I get around this?
I tried to implement the map callback "viewForAnnotation" to check for "VoiceMemoryAnnotation" and I try to return a new "MkAnnotationView" and set it's callout = YES, but I start to get a segmentation fault when I do this.
Any ideas what I"m doing wrong?
First you need to create your annotation object (the one that implements the MKAnnotation protocol) and add it to your map using something like
VoiceMemoryAnnotation*VMA = [[VoiceMemoryAnnotation alloc] init];
VMA.title = #"Title String";
VMA.subtitle = #"Subtitle String";
[self.mapView addAnnotation:VMA];
That will automatically call the following method which you will need to implement:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView*singleAnnotationView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:nil];
singleAnnotationView.canShowCallout = YES;
return singleAnnotationView;
}
In this implementation MKAnnotationView won't work, it needs to be MKPinAnnotationView.
I'm not sure I completely understand your question, but I wonder, is MKMapViews's - (void)selectAnnotation:(id <MKAnnotation>)annotation animated:(BOOL)animated what you're looking for?
In my app am adding subtitle to annotation pin but don't want to show with pin. I am just using it for coding purpose. How can I hide or change subtitle color?
If I understand your question correctly, you are using pin subtitle for some processing and doesn't want to show it on pin. If you are using default callout then it is not possible to change colour of subtitle or to hid it once you assign annotation.subtitle = #"some subtitle".
I would suggest, add another variable to annotation and assign subtitle to that variable (annotation.newSubtitle = #"some subtitle")and don't assign anything for subtitle attribute. All the processing you want to do then can be done with annotation.newSubtitle.
If you are reluctant to add new variable to annotation then you will have to implement custom callout which is tedious.
EDIT:
You will find following useful to assign NSString to your annotation.
#interface MyAnnotation : NSObject<MKAnnotation> {
NSString *newSubTitle;
}
#property(retain,readwrite, nonatomic) NSString *newSubTitle ;
#end
In implementation part
#implementation MyAnnotation
#synthesize mSubTitle;
-(void)setNewSubTitle:(NSString *)SubTitle{
self.newSubTitle = SubTitle;
}
#end
And for assigning
[annotation setNewSubTitle: #"some text"];
for accessing
myString = annotation.newSubtitle
If you want access any string for every specific annotation, you can use NSMutableDictionary and add your annotation as key. and later you could access this in
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
Actually I'm developing something in my app which displays annotations within my MKMapView and those annotations should be clickable and pass a param to a method.
The problem is for now how to pass the parameter to the next function when tapping the disclosure button.
For the formatting of the annotations I use the - (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id <MKAnnotation>)annotation method to display the disclosure button. But the problem is, how to get values out of the Annotation object which is displayed at the moment?
My code for the Annotation (only the relevant part):
#synthesize coordinate, id, title, subtitle;
+ (id)annotationWithCoordinate:(CLLocationCoordinate2D)coordinate {
return [[[[self class] alloc] initWithCoordinate:coordinate] autorelease];
}
- (id)initWithCoordinate:(CLLocationCoordinate2D)coordinate {
self = [super init];
if(nil != self) {
self.coordinate = coordinate;
}
return self;
}
And when touching the disclosure button the method - (void)displayInfo:(NSNumber *) anId; should be called with handing over Annotation.id as the anId param.
The disclosure button is generated with that code:
UIButton *disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
dropPin.rightCalloutAccessoryView = disclosureButton;
dropPin.animatesDrop = YES;
dropPin.canShowCallout = YES;
So - the question:
How can I pass that param to the function OR read the id of the clicked Annotation?
I thought I could do the handling of the tap on disclosureButton either with a selector for UIButton (but - how to pass the id?) or with - (void)mapView:(MKMapView *)mapView
annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control (again - how to pass the id?).
I hope you know what I mean and very big thanks in advance for your answers! :)
In calloutAccessoryControlTapped the annotation is available via the view:
MKAnnotation *annotation = view.annotation;
Wanted some help with a problem with mapkit I am facing. Should be a silly problem or I have missed out something while going through the mapkit framework.
Here is the senario.
I am placing multiple annotation on the map when the user performs some search like pizza.
Added button for the right annotation view, on click which opens a next detail view. The problem is how to send some information to the next view, for example I add index to annotations while creating them, now I want to access this information from annotation, and pass it to the next view via the selector set on the button.
I have checked all the mapkit delicate, but don't find a one where I can map this information with the next view and annotation.
Hope I have not confused you guys in my question. Please let me know I will reframe it.
Thaking in advance.
When you create the UIButton for the annotation, set the tag property (tag is an NSInteger property of UIView) to an id or array index that identifies the relevant object. You can then retrieve that tag value from the sender parameter to your selector.
Edit: here's some sample code.
You create your annotation view and associate the button in your delegate's -mapView:viewForAnnotation: method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
// Boilerplate pin annotation code
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.map dequeueReusableAnnotationViewWithIdentifier: #"restMap"];
if (pin == nil) {
pin = [[[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"restMap"] autorelease];
} else {
pin.annotation = annotation;
}
pin.pinColor = MKPinAnnotationColorRed
pin.canShowCallout = YES;
pin.animatesDrop = NO;
// now we'll add the right callout button
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
// customize this line to fit the structure of your code. basically
// you just need to find an integer value that matches your object in some way:
// its index in your array of MKAnnotation items, or an id of some sort, etc
//
// here I'll assume you have an annotation array that is a property of the current
// class and we just want to store the index of this annotation.
NSInteger annotationValue = [self.annotations indexOfObject:annotation];
// set the tag property of the button to the index
detailButton.tag = annotationValue;
// tell the button what to do when it gets touched
[detailButton addTarget:self action:#selector(showDetailView:) forControlEvents:UIControlEventTouchUpInside];
pin.rightCalloutAccessoryView = detailButton;
return pin;
}
Then in your action method, you'll unpack the value from tag and use it to display the right detail:
-(IBAction)showDetailView:(UIView*)sender {
// get the tag value from the sender
NSInteger selectedIndex = sender.tag;
MyAnnotationObject *selectedObject = [self.annotations objectAtIndex:selectedIndex];
// now you know which detail view you want to show; the code that follows
// depends on the structure of your app, but probably looks like:
MyDetailViewController *detailView = [[MyDetailViewController alloc] initWithNibName...];
detailView.detailObject = selectedObject;
[[self navigationController] pushViewController:detailView animated:YES];
[detailView release];
}
In the Annotation view, is it possible to grab,say, the Title or Subtitle or any other information you used while creating pins? What i am looking to do is have a certain image popup in the annotation based on one of those variables.
#import "MapPin.h"
#implementation MapPin
#synthesize coordinate;
#synthesize title;
#synthesize subtitle;
#synthesize indexnumber;
#synthesize imageFile;
-(id)initWithCoordinates:(CLLocationCoordinate2D)location
placeName: placeName
description:description
indexnum:indexnum
imageFileLoc:imageFileLoc{
self = [super init];
if (self != nil) {
imageFile=imageFileLoc;
[imageFile retain];
indexnumber=indexnum;
[indexnumber retain];
coordinate = location;
title = placeName;
[title retain];
subtitle = description;
[subtitle retain];
}
return self;
}
-(void)addAnnotations {
// Normally read the data for these from the file system or a Web service
CLLocationCoordinate2D coordinate = {35.9077803, -79.0454936};
MapPin *pin = [[MapPin alloc]initWithCoordinates:coordinate
placeName:#"Keenan Stadium"
description:#"Tar Heel Football"
indexnum:#"1"
imageFileLoc:#"owl.jpg"];
[self.map addAnnotation:pin];
Another option:
You can implement these methods:
- (void)mapView:(MKMapView *)mapView didSelectAnnotation:(MKAnnotationView *)view;
- (void)mapView:(MKMapView *)mapView didDeselectAnnotation:(MKAnnotationView *)view;