MKMapView show DetailView - How to make a segue - ios

I have a MapView, the annotations comes from a property list.
Now I would like to see more data in the detail view.
Unfortunately I do not how to program the Segue, so that data can be displayed.
I hope you understand what I mean. My english is not so good ...
I know that there is missing some in the method prepareforSegue.
I am using storyboard.
In advance thank you for your help ...
regards
#import "MapViewNewController.h"
#import "MapViewAnnotation.h"
#import "SetCardController.h"
#interface MapViewNewController ()
#end
#implementation MapViewNewController
#synthesize recipesDictionary = _recipesDictionary;
#synthesize mapView;
#synthesize locationManager;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.delegate = self;
[self.mapView setShowsUserLocation:YES];
MKCoordinateRegion newRegion ;
newRegion.center.latitude = 50.080635;
newRegion.center.longitude = 8.518717;
newRegion.span.latitudeDelta = 15.5;
newRegion.span.longitudeDelta = 15.5;
[mapView setRegion:newRegion animated:YES];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Rezepte" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"myRecipes"];
for(int i = 0; i < [anns count]; i++) {
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"longitude"] floatValue];
MapViewAnnotation *myAnnotation = [[MapViewAnnotation alloc] init];
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = realLatitude;
theCoordinate.longitude = realLongitude;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = [[anns objectAtIndex:i] objectForKey:#"blogname"];
myAnnotation.subtitle = [[anns objectAtIndex:i] objectForKey:#"blogger"];
[mapView addAnnotation:myAnnotation];
}
}
-(MKAnnotationView *)mapView:(MKMapView *)view viewForAnnotation:(id<MKAnnotation>)annotation {
MKPinAnnotationView *pin = nil;
if ([annotation isKindOfClass:[MapViewAnnotation class]]) {
NSString *pinIdentifier = #"myPin";
pin = (MKPinAnnotationView*)[view dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
if (!pin) {
pin = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
pin.image = [UIImage imageNamed:#"pin2.png"];
pin.canShowCallout = YES;
pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
}
return pin;
}
//if Button pressed
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
[self performSegueWithIdentifier:#"showPinDetails" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showPinDetails"]) {
// SetCardController *scc = [segue destinationViewController];
}
}

When you performSegueWithIdentifier:sender from a callout inside annotationView:view, the sender can be the view.annotation property, which uniquely identifies the callout that the user just tapped.

I believe you didn't set the identifier for the Segue in Interface Builder. I tried your code and it worked for me (Xcode 4.4 iOS 5.1 SDK)

The above two answers help, however don't help you identify the unique pins details that I am assuming are part of your p-list.
All Views inherit a the property of a Tag. It can used this to hold a pin index (or key)and therefore pass this to the destination View Controller to uniquely identify the data you hold.
As your button in the call-out view inherits from view you can tag it, and pass the index when its active in the method didselectAnnotationView. The if statement used de-selects the User location pin.
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if (![(PointAnnotation *)view.annotation isKindOfClass:[MKUserLocation class]])
{
PointAnnotation *myAnn = (PointAnnotation *)view.annotation;
detailButton.tag = myAnn.pinIndex;
NSLog (#"Pinindex = %d", myAnn.pinIndex);
}
}
The detailButton.tag can now be used as a parameter in segue selection.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Post data to destination VC
if ([[segue identifier] isEqualToString:#"clubsToDetail"])
{
ClubsMasterData *current = [[ClubsMasterxmlParser ClubsMasterDatas] objectAtIndex:detailButton.tag];
ClubsDetailViewController *DVC = [[ClubsDetailViewController alloc] init];
DVC = [segue destinationViewController];
DVC.linkURL = current.linkURL;
DVC.distance = current.distance;
}
}
Here I indexed an Array rather than a Plist, but the principal is the same.

Related

Pass value of an array pin annotation to JSON

I am new to xcode and i hope someone can help me with my code, and point me how to solve my issue. i've been looking at the internet, but really i couldnt figure it out.
How can i go from a map view to a detail view, bringing the "ID" of the pin with it and print it in a label.
when i click on the arrow for the annotation, my simulator crashes.
Please any ideas, how to approach it and solve it ?
( basicly i want to get the ID of the pin, and send it to a server through JSON and then move to the Detailview, but the next step )
Here is what i did so far :
My Location.m ( view controller for the map )
#import "My_Location.h"
#import "DetailViewController.h"
// ------ FUNCTION TO GET COORDINATES IN JSON FORMAT -------
- (void)retrieveData_Location
{
NSURL * url = [NSURL URLWithString:#"............./Pharmacies.php"];
NSData * data = [NSData dataWithContentsOfURL:url];
NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSMutableArray *newAnnotations = [NSMutableArray array];
CLLocationCoordinate2D location;
for (NSDictionary *dictionary in array)
{
MKPointAnnotation *newAnnotation = [[MKPointAnnotation alloc] init];
location.latitude = [dictionary[#"lat"] doubleValue];
location.longitude = [dictionary[#"lon"] doubleValue];
newAnnotation.title = dictionary[#"name"];
newAnnotation.coordinate = location;
[newAnnotations addObject:newAnnotation];
[newAnnotation release];
}
[self.MyLocation addAnnotations:newAnnotations];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *identifier = #"myAnnotation";
MKPinAnnotationView * annotationView = (MKPinAnnotationView*)[self.MyLocation dequeueReusableAnnotationViewWithIdentifier:identifier];
if (!annotationView)
{
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.pinColor = MKPinAnnotationColorPurple;
annotationView.animatesDrop = YES;
annotationView.canShowCallout = YES;
}else {
annotationView.annotation = annotation;
}
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
return annotationView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:#"Details" sender:view];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Details"])
{
MKAnnotationView *annotationView = sender;
[segue.destinationViewController setAnnotation:annotationView.annotation];
}
}
Take_Photo is my detailviewcntroller.
And my storybaord look like this :
Mylocation View controller -> ( Segue: identifier ( Take_Photo )) -> Take_Photo view Controller

Building MapView Annotations is very slow

I have an application which shows something around 100 Annotations (with custom pin-images, callout accessories and annotation-images) in a MapView. While building the annotations I store a link between annotation and building so I can assign the right building and open the right segue afterwards.
In iOS 6 they get built really fast, I also enabled animation while adding them, so one pin got dropped after the other, but with apple maps in iOS7 this isn't possible anymore (?). Now building those 100 annotations takes over 1 second on my iPhone 4S and that's too long. Is there anyway to improve the code?
- (void)viewDidLoad
...
//creating annotations
annotationlink = [[NSMutableArray alloc] init];
for (int i = 0; i < data.count; i++) {
NSDictionary *dataItem = [data objectAtIndex:i];
//storing annotation in array for link
Annotation *buildingannotation = [[Annotation alloc] init];
NSNumber *index = [NSNumber numberWithInteger:i];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:index, indexKey, buildingannotation, annotationKey, nil];
[annotationlink addObject:dict];
buildingannotation.title = [dataItem objectForKey:#"Building"];
buildingannotation.subtitle = [dataItem objectForKey:#"Info"];
MKCoordinateRegion buildingcoordinates;
buildingcoordinates.center.latitude = [[dataItem objectForKey:#"Latitude"] floatValue];
buildingcoordinates.center.longitude = [[dataItem objectForKey:#"Longitude"] floatValue];
buildingannotation.coordinate = buildingcoordinates.center;
[self.mapView addAnnotation:buildingannotation];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
MKAnnotationView *pinView = (MKAnnotationView *)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
MKAnnotationView *customAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
customAnnotationView.canShowCallout = YES;
//right button to detail view
UIButton* disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customAnnotationView.rightCalloutAccessoryView = disclosureButton;
//left button for image
NSInteger *buildingindex = [self getIndex:annotation];
NSDictionary *dataItem = [data objectAtIndex:buildingindex];
NSString* filename = [dataItem objectForKey:#"Thumb"];
filename = [filename stringByAppendingString:#"#2x.jpg"];
NSString* resourceimagePath = [resourcePath stringByAppendingPathComponent:filename];
Image = [UIImage imageWithContentsOfFile:resourceimagePath];
UIImageView *AnnotationThumb = [[UIImageView alloc] initWithImage:Image];
AnnotationThumb.frame = CGRectMake(0, 0, 31, 31);
customAnnotationView.leftCalloutAccessoryView = AnnotationThumb;
//annotation image
customAnnotationView.image = [UIImage imageNamed:#"Annotation_white.png"];
return customAnnotationView;
return pinView;
}
the following function gets the index of the current annotation using nspredicate to filter the array with the dictionaries. the advantage of this is the fact, that i can also use it when calloutAccessoryControlTapped:
-(NSInteger*) getIndex:(Annotation*)searchannotation
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", annotationKey, searchannotation];
NSArray *filteredarray = [annotationlink filteredArrayUsingPredicate:predicate];
NSDictionary *building = [filteredarray objectAtIndex:0];
NSInteger *buildingIndex = [[building objectForKey:indexKey] integerValue];
return buildingIndex;
}
With an iPhone 4S the last pin is built 1.14 seconds after the view gets loaded.
if i search the annotation link array manually instead of using nspredicate function like this:
//left button for image
int buildingIndex;
for (int i = 0; i < annotationlink.count; i++) {
NSDictionary *annotationDict = [annotationlink objectAtIndex:i];
if ([[annotationDict objectForKey:annotationKey] isEqual:annotation]) {
buildingIndex= [[annotationDict objectForKey:indexKey] integerValue];
i = annotationlink.count;
}
}
NSDictionary *dataItem = [data objectAtIndex:buildingIndex];
the log says that the last pin is built 1.89 seconds after the viewDidLoad.
if i create the annotations in viewDidApper instead of viewDidLoad the View is shown off course immediately but the background takes some time to load so until the pins are dropped everything is gray which is also not very nice...
Thank you Anna for your suggestions! I implemented the improvements like this:
Annotation.h:
#import <MapKit/MKAnnotation.h>
#interface Annotation : NSObject <MKAnnotation> {}
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
#property(nonatomic, copy) NSString *subtitle;
#property NSInteger *ID;
#end
Annotation.m:
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate, title, subtitle, ID;
#end
ViewDidAppear:
//creating annotations
for (int i = 0; i < data.count; i++) {
NSDictionary *dataItem = [data objectAtIndex:i];
Annotation *buildingannotation = [[Annotation alloc] init];
buildingannotation.ID = i;
buildingannotation.title = [dataItem objectForKey:#"Building"];
buildingannotation.subtitle = [dataItem objectForKey:#"Subtitle"];
MKCoordinateRegion buildingcoordinates;
buildingcoordinates.center.latitude = [[dataItem objectForKey:#"Latitude"] floatValue];
buildingcoordinates.center.longitude = [[dataItem objectForKey:#"Longitude"] floatValue];
buildingannotation.coordinate = buildingcoordinates.center;
[self.mapView addAnnotation:buildingannotation];
}
viewForAnnotation:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
MKAnnotationView *pinView = (MKAnnotationView *)
[self.mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
if (pinView == nil) {
MKAnnotationView *customAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
customAnnotationView.canShowCallout = YES;
//right button to detail view
UIButton* disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customAnnotationView.rightCalloutAccessoryView = disclosureButton;
//left button for image
Annotation *buildingAnnotation = (Annotation *)annotation;
NSInteger *buildingindex = buildingAnnotation.ID;
NSString *filePath = [thumbname objectAtIndex:buildingindex];
UIImageView *AnnotationThumb = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:filePath]];
AnnotationThumb.frame = CGRectMake(0, 0, 31, 31);
customAnnotationView.leftCalloutAccessoryView = AnnotationThumb;
//annotation image
customAnnotationView.image = [UIImage imageNamed:#"Annotation_white.png"];
return customAnnotationView;
} else {
pinView.annotation = annotation;
}
return pinView;
}
Callout:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
Annotation *buildingAnnotation = (Annotation *)view.annotation;
selectedbuilding = buildingAnnotation.ID;
[self performSegueWithIdentifier:#"DetailViewController" sender:self];
}
Takes still some time for showing all Annotations. Is there any chance to further improve the code?
I updated the vievForAnnotation function regarding to Anna's reply and the PhotosByLocation Sample Application. It works now and I hope it's the correct way to implement the reuse...
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
MKAnnotationView *buildingAnnotationView = (MKAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:pinIdentifier];
if (buildingAnnotationView) {
[buildingAnnotationView prepareForReuse];
} else {
buildingAnnotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier];
buildingAnnotationView.canShowCallout = YES;
//right button to detail view
UIButton* disclosureButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
buildingAnnotationView.rightCalloutAccessoryView = disclosureButton;
//annotation image
buildingAnnotationView.image = [UIImage imageNamed:#"Annotation_white.png"];
}
//left button for image
Annotation *buildingAnnotation = (Annotation *)annotation;
NSInteger *buildingindex = buildingAnnotation.ID;
NSString *filePath = [thumbname objectAtIndex:buildingindex];
UIImageView *AnnotationThumb = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:filePath]];
AnnotationThumb.frame = CGRectMake(0, 0, 31, 31);
buildingAnnotationView.leftCalloutAccessoryView = AnnotationThumb;
return buildingAnnotationView;
}

Changing pin color when selecting annotation pin

My app is a map view where the user can enter an address which will put a purple pin on the map for the HQ. Secondly, the user can enter any address, which will put a red pin on the map. I would like to be able to change the pin color of the red pins to either red, green, or purple.
I stumbled across a tutorial that will allow the user to select an annotation pin and change its pin color by displaying a modal view. I followed the tutorial meticulously, but for some reason, it is not working correctly. The modal view with the pin selection is displayed, but when a pin color is selected, the pin color on the map view is not updated. Additionally, instead of using "png" images to display custom pins, I would like to use the built-in standard pins (since that's all I need). How can I adjust my code below to achieve this? I added my entire code.
FieldMapController.m
#import "FieldMapController.h"
#import "CustomAnnotation.h"
#define HQ_latitude #"headquarters_latitude"
#define HQ_longitude #"headquarters_longitude"
#define HQ_coordinates #"headquarters_coordinates"
#import "PinSelectionViewController.h"
#interface FieldMapController ()
#end
#implementation FieldMapController
#synthesize mapView;
#synthesize searchBar;
#synthesize geocoder = _geocoder;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
//ACCESS SAVED DATA FROM NSUSERDEFAULTS
-(void)viewWillAppear:(BOOL)animated{
NSUserDefaults *uDefaults = [NSUserDefaults standardUserDefaults];
if ([uDefaults boolForKey:#"headquarters_coordinates"])
{
CLLocationCoordinate2D savedCoordinate;
savedCoordinate.latitude = [uDefaults doubleForKey:#"headquarters_latitude"];
savedCoordinate.longitude = [uDefaults doubleForKey:#"headquarters_longitude"];
NSLog(#"Your HQ is at coordinates %f and %f",savedCoordinate.latitude, savedCoordinate.longitude);
CustomAnnotation *annHq =[[CustomAnnotation alloc] init];
annHq.title=#"HQ";
annHq.subtitle=#"";
annHq.coordinate= savedCoordinate;
[mapView addAnnotation:annHq];
MKCoordinateRegion viewRegion = {{0.0, 0.0}, {0.0, 0.0}};
viewRegion.center.latitude = savedCoordinate.latitude;
viewRegion.center.longitude = savedCoordinate.longitude;
viewRegion.span.longitudeDelta = 0.5f;
viewRegion.span.latitudeDelta = 0.5f;
[self.mapView setRegion:viewRegion animated:YES];
[self.mapView setDelegate:self];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.mapView.delegate = self;
self.searchBar.delegate = self;
//SEARCH BAR TOOLBAR WITH "DONE" AND "CANCEL" BUTTON
UIToolbar* searchToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 50)];
searchToolbar.barStyle = UIBarStyleBlackTranslucent;
searchToolbar.items = [NSArray arrayWithObjects:
[[UIBarButtonItem alloc]initWithTitle:#"Cancel" style:UIBarButtonItemStyleBordered target:self action:#selector(cancelSearchBar)],
[[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil],
nil];
[searchToolbar sizeToFit];
searchBar.inputAccessoryView = searchToolbar;
}
//WHEN PUSHING THE "CANCEL" BUTTON IN THE SEARCH BAR
-(void)cancelSearchBar
{
[searchBar resignFirstResponder];
searchBar.text = #"";
}
//PREPARE SEGUE FOR THE PIN SELECTOR VIEW
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"ShowPinChoicesSegue"])
{
PinSelectionViewController *pinVC = [segue destinationViewController];
CustomAnnotation *selectedAnnotation = (CustomAnnotation *)sender;
pinVC.currentPinType = selectedAnnotation.pinType;
pinVC.delegate = self;
}
}
//WHAT HAPPENS WHEN THE "SEARCH" BUTTON AT THE SEARCH BAR KEYBOARD IS TAPPED
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
{
//Forward Geocoder
if (!self.geocoder)
{
self.geocoder = [[CLGeocoder alloc] init];
}
NSString *address = [NSString stringWithFormat:#"%#", self.searchBar.text];
[self.geocoder geocodeAddressString:address completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D coordinate = location.coordinate;
//Display Coordinates in Console
NSLog (#"%f %f", coordinate.latitude, coordinate.longitude);
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.01;
span.longitudeDelta = 0.01;
region.span = span;
region.center = coordinate;
//Create Annotation with Callout Bubble that displays "No Information"
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
[annotation setCoordinate:coordinate];
[annotation setTitle:#"No Information"];
[[self mapView] addAnnotation:annotation];
[self.mapView setRegion:region animated:TRUE];
[self.mapView regionThatFits:region];
//Dismiss the Search Bar Keyboard
[self.searchBar resignFirstResponder];
//Delete text in Search Bar
self.searchBar.text = #"";
}
}];
}
//CUSTOM ANNOTATION VIEW
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
{
return nil;
}
if ([annotation isKindOfClass:[CustomAnnotation class]])
{
MKPinAnnotationView *annotationView =
(MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:((CustomAnnotation *)annotation).annotationViewImageName];
if(annotationView == nil)
{
MKPinAnnotationView *customPinView =
[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:((CustomAnnotation *)annotation).annotationViewImageName];
if([[customPinView.annotation title] isEqualToString:#"HQ"])
{
//The pin for the HQ should be purple
customPinView.pinColor = MKPinAnnotationColorPurple;
}
else
{
//All other new pins should be "red" by default
customPinView.image = [UIImage imageNamed:((CustomAnnotation *)annotation).annotationViewImageName];
}
customPinView.canShowCallout = YES;
customPinView.animatesDrop = YES;
//Right Callout Accessory Button
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
}
else
{
annotationView.annotation = annotation;
}
return annotationView;
}
return nil;
}
//SHOW ACCESSORY VIEW WHEN BUTTON ON CALLOUT BUBBLE IS TAPPED
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
if (![view.annotation isKindOfClass:[CustomAnnotation class]])
return;
CustomAnnotation *customAnnotation = (CustomAnnotation *)view.annotation;
if (control.tag == 0)
{
[self performSegueWithIdentifier:#"ShowPinChoicesSegue" sender:customAnnotation];
}
else
{
[self onRightCalloutAccessoryViewTouched:control];
}
}
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
if(![view.annotation isKindOfClass:[CustomAnnotation class]])
return;
if (!view.rightCalloutAccessoryView)
{
UIButton *rightViewButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 48.0, 32.0)];
[rightViewButton addTarget:self action:#selector(onRightCalloutAccessoryViewtouched:) forControlEvents:UIControlEventTouchUpInside];
rightViewButton.tag = 1;
view.rightCalloutAccessoryView = rightViewButton;
}
}
-(void)onRightCalloutAccessoryViewTouched:(id)sender
{
CustomAnnotation *selectedAnnotation = (CustomAnnotation *)[mapView.selectedAnnotations objectAtIndex:0];
[self performSegueWithIdentifier:#"ShowPinChoicesSegue" sender:selectedAnnotation];
}
- (void)viewDidUnload
{
self.mapView = nil;
self.searchBar = nil;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
//BUTTON TO SELECT NEW HQ
- (IBAction)selectHq:(UIBarButtonItem *)sender
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Select Headquarters"
message:#"Enter Address"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
alert.alertViewStyle = UIAlertViewStylePlainTextInput;
[[alert textFieldAtIndex:0] setKeyboardType:UIKeyboardTypeDefault];
[alert show];
}
//REMOVING ALL PINS EXCEPT USER LOCATION
- (IBAction)resetPins:(UIBarButtonItem *)sender
{
id userLocation = [mapView userLocation];
NSMutableArray *pins = [[NSMutableArray alloc] initWithArray:[mapView annotations]];
if ( userLocation != nil )
{
[pins removeObject:userLocation]; //avoid removing user location
}
[mapView removeAnnotations:pins];
pins = nil;
[[NSUserDefaults standardUserDefaults] removeObjectForKey:HQ_coordinates];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:HQ_longitude];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:HQ_latitude];
}
//ALERT VIEW TO ENTER ADDRESS OF HQ
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != alertView.cancelButtonIndex)
{
UITextField *field = [alertView textFieldAtIndex:0];
field.placeholder = #"Enter HQ Address";
if (!self.geocoder)
{
self.geocoder = [[CLGeocoder alloc] init];
}
NSString *hqAddress = [NSString stringWithFormat:#"%#", field.text];
[self.geocoder geocodeAddressString:hqAddress completionHandler:^(NSArray *placemarks, NSError *error) {
if ([placemarks count] > 0)
{
CLPlacemark *placemark = [placemarks objectAtIndex:0];
CLLocation *location = placemark.location;
CLLocationCoordinate2D hqCoordinate = location.coordinate;
NSLog (#"Your new HQ is at coordinates %f and %f", hqCoordinate.latitude, hqCoordinate.longitude);
MKCoordinateRegion region;
MKCoordinateSpan span;
span.latitudeDelta = 0.01;
span.longitudeDelta = 0.01;
region.span = span;
region.center = hqCoordinate;
MKPointAnnotation *hqAnnotation = [[MKPointAnnotation alloc] init];
[hqAnnotation setCoordinate:hqCoordinate];
[hqAnnotation setTitle:#"HQ"];
[[self mapView] addAnnotation:hqAnnotation];
[self.mapView setRegion:region animated:TRUE];
[self.mapView regionThatFits:region];
//Save to NSUserDefaults
NSUserDefaults *uDefaults = [NSUserDefaults standardUserDefaults];
[uDefaults setDouble:hqCoordinate.latitude forKey:HQ_latitude];
[uDefaults setDouble:hqCoordinate.longitude forKey:HQ_longitude];
[uDefaults setBool:YES forKey:HQ_coordinates];
[uDefaults synchronize];
}
}];
}
else
{
//any actions for "Cancel"
}
}
//DEFINES WHAT SELECTING THE NEW PIN COLOR DOES
-(void)userDidSelectPinType:(AnnotationPinType)aPinType
{
CustomAnnotation *selectedAnnotation = (CustomAnnotation *)[mapView.selectedAnnotations objectAtIndex:0];
selectedAnnotation.pinType = aPinType;
[mapView removeAnnotation:selectedAnnotation];
[mapView addAnnotation:selectedAnnotation];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
#end
CustomAnnotation.m
#import "CustomAnnotation.h"
#import <CoreLocation/CoreLocation.h>
#implementation CustomAnnotation
#synthesize title, subtitle, coordinate;
#synthesize pinType;
-(id) initWithCoordinate:(CLLocationCoordinate2D)aCoordinate title:(NSString *)aTitle subtitle:(NSString *)aSubtitle
{
if ((self = [super init]))
{
self.title = aTitle;
self.coordinate = aCoordinate;
self.subtitle = aSubtitle;
}
return self;
}
- (NSString *)annotationViewImageName
{
switch (self.pinType)
{
case 0:
return #"Red_Pin.png";
break;
case 1:
return #"Green_Pin.png";
break;
case 2:
return #"Purple_Pin.png";
break;
default:
break;
}
}
- (NSString *)title
{
return title;
}
- (NSString *)subtitle
{
return subtitle;
}
#end
PinSelectionViewController.m
#import "PinSelectionViewController.h"
#interface PinSelectionViewController ()
#end
#implementation PinSelectionViewController
#synthesize delegate;
#synthesize currentPinType;
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Table view data source
- (void)tableView:(UITableView *) tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row ==currentPinType)
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate userDidSelectPinType:indexPath.row];
}
#end
PinSelectionDelegateProtocol.h
#import <Foundation/Foundation.h>
typedef enum
{
RED_PIN,
GREEN_PIN,
PURPLE_PIN
} AnnotationPinType;
#protocol PinSelectionDelegate <NSObject>
#required
-(void)userDidSelectPinType:(AnnotationPinType)aPinType;
#end
The problem I see is early on in your viewForAnnotation method. You correctly reuse annotationviews but incorrectly assume that the reused view is configured properly. When you check if the view is nil you only configure brand new ones to have the pin colour that matches the annotation's name. What you need to do is check if it is nil, if is then make a new one and close that if. Then make sure both new and reused annotationviews have their pin colour, title, accessory view etc set up as you want it for that annotation.
Also you can't set the .image of an MKPinAnnotationView, it'll just get overwritten. If you really want to set a custom image you have to use a regular MKAnnotationView. If you are ok with using the standard red, green and blue pins then just set the .pinColor to whatever you want.

how to display data from plist in detail view using segue when map view annotation is pressed

as the title describes i am having trouble displaying the data that i have in my plist for annotations in the map view project i am working on using segue in storyboard. i have the map view and all the annotations (pins) are showing their co ordinates properly. now i want to use segue to transition into a detail view when the user has pressed the detail disclosure button. so far i have the following which is an empty perform segue:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:#"Details" sender:view];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
}
the details i want to display in detail view are declared as strings. as follows
#interface ItalyMapDetail : UIViewController{
IBOutlet UIImageView *appImage;
IBOutlet UITextView *appText;
IBOutlet UILabel *appName;
NSString *appImageString;
NSString *appTextString;
NSString *appNameString;
}
#property (nonatomic, retain) NSString *appImageString;
#property (nonatomic, retain) NSString *appTextString;
#property (nonatomic, retain) NSString *appNameString;
#property (nonatomic, strong) UIImage* image;
#end
and in .m file it is as follows
#synthesize appImageString, appTextString, appNameString;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
appImage.image = [UIImage imageNamed:appImageString];
appText.text = appTextString;
appName.text = appNameString;
}
which is truly generic declaration of some strings in detail view. the segue identifier fires and the view transitions to detail view and back properly since i have a navigation controller already. now i know how to declare the strings in prepare for segue when it comes to tableview but i don't know how to declare these details from my plist in the segue in order to display the annotation details in the detail view controller. can anyone give me a hand with this.
oh by the way here is the decleration of the annotations which i derive from plist in my view did load if that helps.
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Map" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"Italy"];
for(int i = 0; i < [anns count]; i++) {
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"Latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"Longitude"] floatValue];
Annotation *myAnnotation = [[Annotation alloc] init];
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = realLatitude;
theCoordinate.longitude = realLongitude;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = [[anns objectAtIndex:i] objectForKey:#"Title"];
myAnnotation.subtitle = [[anns objectAtIndex:i] objectForKey:#"Subtitle"];
[myMapView addAnnotation:myAnnotation];
[annotations addObject:myAnnotation];
}
i know this might be very easy for most of you guys out there but i am truly stuck and i can use some help in code level.
Edit 1:
here is the code after the changes suggested :
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"ItalyMap" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"Italy"];
for(int i = 0; i < [anns count]; i++) {
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"Latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"Longitude"] floatValue];
Annotation *myAnnotation = [[Annotation alloc] init];
CLLocationCoordinate2D theCoordinate;
theCoordinate.latitude = realLatitude;
theCoordinate.longitude = realLongitude;
myAnnotation.coordinate = theCoordinate;
myAnnotation.title = [[anns objectAtIndex:i] objectForKey:#"Title"];
myAnnotation.subtitle = [[anns objectAtIndex:i] objectForKey:#"Subtitle"];
[myMapView addAnnotation:myAnnotation];
[annotations addObject:myAnnotation];
myAnnotation.annotationData = [anns objectAtIndex:i];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"view for annotation works");
if([annotation isKindOfClass:[MKUserLocation class]])
return nil;
static NSString *annotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *) [mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier];
if (!pinView)
{
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
[pinView setPinColor:MKPinAnnotationColorGreen];
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIImageView *iconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"acolon1.png"]];
pinView.leftCalloutAccessoryView = iconView;
pinView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
// Keep a reference to the callout's annotation data
self.selectedAnnotationData = [(Annotation *)view.annotation annotationData];
[self performSegueWithIdentifier:#"Details" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"Details"])
{
ItalyMapDetail *controller = segue.destinationViewController;
controller.annotationData = self.selectedAnnotationData;
}
}
now i have added the dic to the annotation and detail view is imported. the only thing that is not showing still is the strings and the image in the detail view.
You could pass the particular data object to the Annotation using
myAnnotation.annotationData = [anns objectAtIndex:i];
Then in prepareForSegue:sender: you could access the annotation data and pass it to the detail view controller.
Edit: Added sample code
Declare a property on the Annotation class:
#property (nonatomic, strong) NSDictionary *annotationData;
Declare a property on the same view controller as your map view:
#property (nonatomic, strong) NSDictionary *selectedAnnotationData;
And implement the map view delegate method:
-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
// Keep a reference to the callout's annotation data
self.selectedAnnotationData = [(Annotation *)view.annotation annotationData];
[self performSegueWithIdentifier:#"MY_SEGUE_IDENTIFIER" sender:self];
}
Then pass the annotation data to the subsequent controller in the prepareForSegue:sender: method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"showPlattegrondDetails"])
{
ItalyMapDetail *controller = segue.destinationViewController;
controller.annotationData = self.selectedAnnotationData;
}
}
Edit 2: Based on edited question.
Without knowing the names of the keys in the plist, I can only point you in the direction you should be looking.
Your ItalyMapDetail controller should have the annotation data dictionary as provided in prepareForSegue:sender: of the previous controller, it should just be a case of using values from that dictionary to populate your UI...
Update your viewDidLoad method of the ItalyMapDetail controller as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
// temporary test: print out the annotation data so
// we can see what we've got. This shouldn't be nil.
NSLog("Annotation Data: %#", self.annotationData);
// replace these keys with appropriate ones for your plist
appImage.image = [UIImage imageNamed:self.annotationData[#"imageName"]];
appText.text = self.annotationData[#"appText"];
appName.text = self.annotationData[#"appName"];
}

Plot two different color of mapview pin

I'm trying to display mapview with an array of coordinates. So, i load the latitudes and longitudes from array where those are contains. It is showing perfectly. Now, i want to display one paritcular latitude & longitude with different color pin. I've referred this answer no such different i can't see.
ViewController.m
-(void)showMap:
{
...
...
DataProvider *d = [DataProvider getInstance];
NSInteger numb = sender.view.tag;
d.colorlatitude = [[arraxy objectAtIndex:numb] objectForKey:#"lat"];
d.colorlongitude = [[arraxy objectAtIndex:numb] objectForKey:#"lng"];
[d._address removeAllObjects];
[arraxy removeObjectAtIndex:sender.view.tag];
d._address = arraxy;
MapViewController *map = [[MapViewController alloc]initWithNibName:#"MapViewController" bundle:nil];
[self.navigationController pushViewController:map animated:YES];
}
MapViewController.m
-(void)viewWillAppear:(BOOL)animated
{
DataProvider *d = [DataProvider getInstance];
[_mapView removeAnnotations:_mapView.annotations];
RegisterViewController *appDelegate = [[RegisterViewController alloc]init];
[_mapView setShowsUserLocation:YES];
[_mapView setRegion:MKCoordinateRegionMakeWithDistance([appDelegate.locationManager location].coordinate, 1000, 1000)];
[_mapView setUserTrackingMode:MKUserTrackingModeNone];
MKCoordinateRegion rregion = {{0.0,0.0},{0.0,0.0}};
rregion.center.latitude = [d.colorlatitude floatValue];
rregion.center.longitude = [d.colorlongitude floatValue];
rregion.span.latitudeDelta=0.001f;
rregion.span.longitudeDelta=0.001f;
[_mapView setRegion:rregion];
MapviewAnnotations *add = [[MapviewAnnotations alloc]init];
add.coordinate = rregion.center;
[_mapView addAnnotation:add];
if (d._address)
{
for (int i=0; i<[d._address count]; i++)
{
NSDictionary *dic=[d._address objectAtIndex:i];
MKCoordinateRegion region={{0.0,0.0},{0.0,0.0}};
region.center.latitude=[[dic objectForKey:#"lat"]floatValue];
region.center.longitude=[[dic objectForKey:#"lng"]floatValue];
region.span.latitudeDelta=0.001f;
region.span.longitudeDelta=0.001f;
[_mapView setRegion:region];
MapviewAnnotations *ann=[[MapviewAnnotations alloc]init];
ann.coordinate=region.center;
[_mapView addAnnotation:ann];
}
}
[super viewWillAppear:YES];
}
and my MKMapView's delegate method is
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if (![annotation isKindOfClass:[MapviewAnnotations class]])
{
return nil;
}
static NSString *reuseId = #"currentloc";
MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annView == nil)
{
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annView.animatesDrop = NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
}
else
{
annView.annotation = annotation;
}
DataProvider *mvAnn = [DataProvider getInstance];
if (mvAnn.colorlatitude) // here i'm checking the condition.
{
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
return annView;
}
I'm just writting the condition if the co-ordinate is there i've to plot the green color pin to that particular coordinate only. How to achieve this?
The difference between your code and the one Hinata has referred you to is that the if statement in the other code uses a value (yesno) on the annotation it is currently drawing to decide what colour to use. You are getting a value from DataProvider but not telling it which annotation you are drawing, so the instance it gives you is just what ever the instance method feels like returning at the time the map is asking for the pin. You need to tell it what you're drawing for it to decide what to put into colorlatitude
I've done this through some flags like below inside of my - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
MapviewAnnotations *mvAnn = (MapviewAnnotations *)annotation;
if (mvAnn.flag == 1)
{
annView.pinColor = MKPinAnnotationColorGreen;
}
else if (mvAnn.flag == 10)
{
annView.pinColor = MKPinAnnotationColorPurple;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
In my viewWillAppear i received the coordinates from DataProvider and assigned with separate MapviewAnnotations with flags and differentiate it with three type of pins simply.
Cheers!

Resources