I have created annotations on a mapview however i would like my user marker to return a flashing blue dot instead of a green pin. I cannot seem to figure out how to change the original user marker with the flashing blue dot. Here is my code.
#import "ViewController.h"
#import "DetailController.h"
#import "Annotation.h"
#import "City.h"
#interface ViewController (){
MKLocalSearch *localSearch;
MKLocalSearchResponse *results;
}
#property (nonatomic, strong) IBOutlet DetailController *detailViewController;
#end
#define getDatalURL #"http://www.club-hop.com/apptest.php"
#implementation ViewController
#synthesize mapView,jsonArray,citiesArray;
- (void)viewDidLoad
{
[super viewDidLoad];
[self retrieveData];
self.detailViewController = [[DetailController alloc] init];
[self.searchDisplayController setDelegate:self];
[self.ibSearchBar setDelegate:self];
//Zoom the map to current location.
[self.mapView setShowsUserLocation:YES];
[self.mapView setUserInteractionEnabled:YES];
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow];
City * cityObject;
// load external page into UIWebView
NSMutableArray * locations= [[NSMutableArray alloc]init];
CLLocationCoordinate2D location;
Annotation * myAnn;
for(int u=0; u<citiesArray.count;u++){
cityObject=[citiesArray objectAtIndex:u];
myAnn=[[Annotation alloc]init];
myAnn.city=cityObject; // Store the city object on the annotation
NSNumber *aLat= cityObject.Latitude;
NSNumber *aLon= cityObject.Longitude;
double lat = [aLat doubleValue];
double lon = [aLon doubleValue];
location.latitude= lat;
location.longitude=lon;
myAnn.coordinate = location;
myAnn.title=cityObject.clubName;
myAnn.subtitle=cityObject.cityName;
[locations addObject:myAnn];}
[self.mapView addAnnotations:locations];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//class methods
-(void) retrieveData{
NSURL * url= [NSURL URLWithString:getDatalURL];
NSData * data= [NSData dataWithContentsOfURL:url];
jsonArray= [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//setup cities array
citiesArray=[[NSMutableArray alloc]init];
for(int i=0; i<jsonArray.count;i++){
NSString * cID= [[jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString * cName= [[jsonArray objectAtIndex:i] objectForKey:#"cityName"];
NSString * cCountry= [[jsonArray objectAtIndex:i] objectForKey:#"cityCountry"];
NSString * cLine= [[jsonArray objectAtIndex:i] objectForKey:#"clubLine"];
NSString * pri=[[jsonArray objectAtIndex:i] objectForKey:#"price"];
NSString * promo=[[jsonArray objectAtIndex:i] objectForKey:#"promo"];
NSString * clName= [[jsonArray objectAtIndex:i] objectForKey:#"clubName"];
NSNumber * cLatitude= [[jsonArray objectAtIndex:i] objectForKey:#"Latitude"];
NSNumber * cLongitude= [[jsonArray objectAtIndex:i] objectForKey:#"Longitude"];
[citiesArray addObject:[[City alloc]initWithCityName:cName andCityCountry:cCountry andClubName:clName andClubLine:cLine andPrice:pri andPromo:promo andLatitude:cLatitude andLongitude:cLongitude andCityId:cID]];
}
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
self.detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"Page2"];
Annotation *myAnnotation=(Annotation *)view.annotation;
self.detailViewController.city=myAnnotation.city;
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *pin=nil;
if ([annotation isKindOfClass:[Annotation class]])
{
pin=(MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"myAnnotation"];
if (pin == nil)
{
pin=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"myAnnotation"];
pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pin.image=[UIImage imageNamed:#"mappin.png"];
pin.centerOffset=CGPointMake(0.0, pin.image.size.height/-2);
pin.canShowCallout=YES;
}
}
return pin;
}
#end
You can achieve this through your viewForAnnotation method in your MKMapViewDelegate. If this method returns nil then the map view will use the default annotation view. You can test the annotation to see if it is your subclass and then return a view or nil as appropriate. Something like -
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *pin=nil;
if ([annotation isKindOfClass:[Annotation class]])
{
pin=(MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"myAnnotation"];
if (pin == nil)
{
pin=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"myAnnotation"];
pin.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pin.image=[UIImage imageNamed:#"mappin.png"];
pin.centerOffset=CGPointMake(0.0, pin.image.size.height/-2);
pin.canShowCallout=YES;
}
}
return pin;
}
Related
I want to display different text on each annotationView, but same value is displayed on every annotation.
Following is my code :
NSMutableArray *strAnnoTitle;
-(void)callAddAnnotations{
cnt = 0;
[_mapView removeAnnotations:[_mapView annotations]];
for (id obj in arrPropTemp) {
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake([[[arrPropTemp valueForKey:#"Latitude"] objectAtIndex:cnt] floatValue], [[[arrPropTemp valueForKey:#"Longitude"] objectAtIndex:cnt] floatValue]);
strAnnoTitle[cnt] = [obj valueForKey:#"ListPriceForMap"];
// Add an annotation
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = coords;
[self.mapView addAnnotation:point];
cnt++;
}
}
-(MKAnnotationView *)createAnnotation:(MKAnnotationView *)viewAn{
UILabel *lbl = (UILabel *)[viewAn viewWithTag:100];
[lbl setText:strAnnoTitle[cnt]];
return viewAn;
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
MKAnnotationView *viewAn = [[[NSBundle mainBundle] loadNibNamed:#"MapAnnotation" owner:self options:nil] lastObject];
viewAn = [self createAnnotation:viewAn];
return viewAn;
return nil;
}
Output:
Where am I getting wrong? How do I solve this?
Change your implementation .
-(void)callAddAnnotations{
cnt = 0;
[_mapView removeAnnotations:[_mapView annotations]];
for (id obj in arrPropTemp) {
CLLocationCoordinate2D coords = CLLocationCoordinate2DMake([[[arrPropTemp valueForKey:#"Latitude"] objectAtIndex:cnt] floatValue], [[[arrPropTemp valueForKey:#"Longitude"] objectAtIndex:cnt] floatValue]);
strAnnoTitle[cnt] = [obj valueForKey:#"ListPriceForMap"];
// Add an annotation
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = coords;
[point setTitle:strAnnoTitle[cnt]];
[self.mapView addAnnotation:point];
cnt++;
}
}
change your create method
-(MKAnnotationView *)createAnnotation:(MKAnnotationView *)viewAn{
UILabel *lbl = (UILabel *)[viewAn viewWithTag:100];
[lbl setText: viewAn.annotation.title];//can move this also to delegate
return viewAn;
}
Change your viewForAnnotation method to like this.
-(MKAnnotationView *)mapView:(MKMapView *)MapView viewForAnnotation:(id<MKAnnotation>)annotation{
static NSString *cabAnnotationIdentifier=#"cabAnnotationIdentifier";
MKAnnotationView * viewAn =[MapView dequeueReusableAnnotationViewWithIdentifier:cabAnnotationIdentifier];
if(!annotationView){
MKAnnotationView *viewAn = [[[NSBundle mainBundle] loadNibNamed:#"MapAnnotation" owner:self options:nil] lastObject];
viewAn = [self createAnnotation:viewAn];
}
return viewAn;
}
Take 1 NSObject file with the required fields u want for annotation. Add annotation on mapview and attach information on that Nsobject file also and same do as in viewforannotation method it will work.
Like
1.) NSObject file
REVPin.h
#interface REVPin : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
NSInteger tag;
NSArray *nodes;
}
#property(nonatomic, retain) NSArray *nodes;
#property(readwrite, nonatomic) NSInteger tag;
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString *title;
-(void)AddPinToMap:(NSMutableArray *)PinArray
{
NSMutableArray *pins = [NSMutableArray array];
oldZoomLevel = (int)[self zoomLevelForMapRect:MapView.visibleMapRect withMapViewSizeInPixels:CGSizeMake(MapView.frame.size.width, MapView.frame.size.height)];
if ([isFirstTimeN isEqualToString:#"YES"]) {
[MapView removeAnnotations:MapView.annotations];
for(int i=0;i<[PinArray count];i++) {
CGFloat latitude=[[[PinArray objectAtIndex:i] objectForKey:#"lat"] floatValue];
CGFloat longitude=[[[PinArray objectAtIndex:i] objectForKey:#"lon"] floatValue];
CLLocationCoordinate2D newCoord = {latitude, longitude};
REVPin *pin = [[REVPin alloc] init];
pin.title = [NSString stringWithFormat:#"Pin %i",i+1];
pin.subtitle = [NSString stringWithFormat:#"Pin %i subtitle",i+1];
pin.coordinate = newCoord;
pin.tag = i;
pin.userId = [NSString stringWithFormat:#"%#",[[PinArray objectAtIndex:i] objectForKey:#"u"]];
[pins addObject:pin];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
REVPin *pin = (REVPin *)annotation;
MKAnnotationView *annView;
if([annotation class] == MKUserLocation.class)
{
return nil;
}
annView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"pin"];
annView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:#"pin"];
[array addObject:pin];
return annView;
}
try by using above way it will work
I am trying to detect which annotation disclosure button is pressed in order to display specific information for that location in DetailController. This information is parsed in JSon, any suggestions on how to detect which annotation is selected and then parse the correct information to DetailController? Here is my ViewController.m file
#import "ViewController.h"
#import "DetailController.h"
#import "Annotation.h"
#import "City.h"
#interface ViewController ()
#property (nonatomic, strong) IBOutlet DetailController *detailViewController;
#end
#define getDatalURL #"http://www.club-hop.com/apptest.php"
#implementation ViewController
#synthesize mapView,jsonArray,citiesArray;
/*-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:#"clubName"]){
NSString * name= #"clubName";
NSString * line= #"clubLine";
DetailViewController *dv= [segue destinationViewController];
dv.cName=name;
dv.cLine=line;
}
}*/
- (void)viewDidLoad
{
[super viewDidLoad];
[self retrieveData];
self.detailViewController = [[DetailController alloc] init];
/* Zoom the map to current location.
[self.mapView setShowsUserLocation:YES];
[self.mapView setUserInteractionEnabled:YES];
[self.mapView setUserTrackingMode:MKUserTrackingModeFollow];*/
City * cityObject;
// load external page into UIWebView
NSMutableArray * locations= [[NSMutableArray alloc]init];
CLLocationCoordinate2D location;
Annotation * myAnn;
for(int u=0; u<citiesArray.count;u++){
cityObject=[citiesArray objectAtIndex:u];
myAnn=[[Annotation alloc]init];
NSNumber *aLat= cityObject.Latitude;
NSNumber *aLon= cityObject.Longitude;
double lat = [aLat doubleValue];
double lon = [aLon doubleValue];
location.latitude= lat;
location.longitude=lon;
myAnn.coordinate = location;
myAnn.title=cityObject.clubName;
myAnn.subtitle=cityObject.cityName;
[locations addObject:myAnn];}
[self.mapView addAnnotations:locations];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//class methods
-(void) retrieveData{
NSURL * url= [NSURL URLWithString:getDatalURL];
NSData * data= [NSData dataWithContentsOfURL:url];
jsonArray= [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
//setup cities array
citiesArray=[[NSMutableArray alloc]init];
for(int i=0; i<jsonArray.count;i++){
NSString * cID= [[jsonArray objectAtIndex:i] objectForKey:#"id"];
NSString * cName= [[jsonArray objectAtIndex:i] objectForKey:#"cityName"];
NSString * cCountry= [[jsonArray objectAtIndex:i] objectForKey:#"cityCountry"];
NSString * cLine= [[jsonArray objectAtIndex:i] objectForKey:#"clubLine"];
NSString * clName= [[jsonArray objectAtIndex:i] objectForKey:#"clubName"];
NSNumber * cLatitude= [[jsonArray objectAtIndex:i] objectForKey:#"Latitude"];
NSNumber * cLongitude= [[jsonArray objectAtIndex:i] objectForKey:#"Longitude"];
[citiesArray addObject:[[City alloc]initWithCityName:cName andCityCountry:cCountry andClubName:clName andClubLine:cLine andLatitude:cLatitude andLongitude:cLongitude andCityId:cID]];
}
}
#pragma mark - MKMapViewDelegate
// user tapped the disclosure button in the callout
//
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
self.detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"Page2"];
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView* pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"pinView"];
if (!pinView)
{
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"pinView"] ;
pinView.pinColor=MKPinAnnotationColorGreen;
pinView.animatesDrop=YES;
pinView.canShowCallout=YES;
UIButton * rightButton= [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView=rightButton;
}
else{
pinView.annotation=annotation;
}
return pinView;
}
#end
Add a City property to both your AnnotationView and your detailViewController classes -
In detailViewController.h and in AnnotationView.h add
#class City;
#property (strong,nonatomic) City *city;
In viewDidLoad for your ViewController.m file replace your current 'for' loop with the following
for (int u=0; u<citiesArray.count;u++) {
cityObject=[citiesArray objectAtIndex:u];
myAnn=[[Annotation alloc]init];
myAnn.city=cityObject; // Store the city object on the annotation
NSNumber *aLat= cityObject.Latitude;
NSNumber *aLon= cityObject.Longitude;
double lat = [aLat doubleValue];
double lon = [aLon doubleValue];
location.latitude= lat;
location.longitude=lon;
myAnn.coordinate = location;
myAnn.title=cityObject.clubName;
myAnn.subtitle=cityObject.cityName;
[locations addObject:myAnn];
}
Alternatively you create a new initWithCity:city method for your annotation and just call that and have the initialiser set up all of the other annotation properties. This would be the new 'for' loop in your viewDidLoad method in ViewController.m
for (int u=0; u<citiesArray.count;u++) {
cityObject=[citiesArray objectAtIndex:u];
myAnn=[[Annotation alloc]initWithCity:cityObject];
[locations addObject:myAnn];
}
In annotation.m
#import "City.h"
-(id)initWithCity:(City *)city
{
self=[super init];
if (self) {
self.city=city; // Store the city object on the annotation
NSNumber *aLat= city.Latitude;
NSNumber *aLon= city.Longitude;
double lat = [aLat doubleValue];
double lon = [aLon doubleValue];
CLLocationCoordinate2D location;
location.latitude= lat;
location.longitude=lon;
self.coordinate = location;
self.title=city.clubName;
self.subtitle=city.cityName;
}
return (self);
}
Either way your calloutAccessoryControlTapped: in ViewController.m becomes -
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle:nil];
self.detailViewController = [storyboard instantiateViewControllerWithIdentifier:#"Page2"];
Annotation *myAnnotation=(Annotation *)view.annotation;
self.detailViewController.city=myAnnotation.city;
[self.navigationController pushViewController:self.detailViewController animated:YES];
}
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"];
}
I'm looking to adapt the following tutorial to create annotations from a plist. I'm brand new to Objective C and Xcode, and tried many different tutorials but can't seem to get this together.
Annotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface Annotation : NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString * title;
#property (nonatomic, copy) NSString * subtitle;
#property (nonatomic, copy) UIImageView * leftCalloutAccessoryView;
#end
Annotation.m
#import "Annotation.h"
#implementation Annotation
#synthesize coordinate, title, subtitle, leftCalloutAccessoryView;
#end
ViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet MKMapView *myMapView;
#end
ViewController.m
#import "ViewController.h"
#import "Annotation.h"
#interface ViewController ()
#end
//Wimbledon Coordinates
#define WIMB_LATITUDE 51.434783;
#define WIMB_LONGITUDE -0.213428;
//Stadium Coordinates
#define ARSENAL_LATITUDE 51.556899;
#define ARSENAL_LONGITUDE -0.106403;
#define CHELSEA_LATITUDE 51.481314;
#define CHELSEA_LONGITUDE -0.190129;
//Span
#define THE_SPAN 0.10f;
#implementation ViewController
#synthesize myMapView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the region
MKCoordinateRegion myRegion;
//Center
CLLocationCoordinate2D center;
center.latitude = ARSENAL_LATITUDE;
center.longitude = ARSENAL_LONGITUDE;
//Span
MKCoordinateSpan span;
span.latitudeDelta = THE_SPAN;
span.longitudeDelta = THE_SPAN;
myRegion.center = center;
myRegion.span=span;
//Set our mapView
[myMapView setRegion:myRegion animated:YES];
//Annotation
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation * myAnn;
//Arsenal Annotation
myAnn = [[Annotation alloc] init];
location.latitude = ARSENAL_LATITUDE;
location.longitude = ARSENAL_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Arsenal FC";
myAnn.subtitle = #"The Gunners";
//myAnn.image = [UIImage imageNamed:#"location.png"];
[locations addObject:myAnn];
//Chelsea Annotation
myAnn = [[Annotation alloc] init];
location.latitude = CHELSEA_LATITUDE;
location.longitude = CHELSEA_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Chelsea FC";
myAnn.subtitle = #"The Blue Lions";
[locations addObject:myAnn];
[self.myMapView addAnnotations:locations];
}
//THIS CODE WORKS FOR CUSTOM ANNOTATIONS
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"redpin"];
newAnnotation.pinColor = MKPinAnnotationColorRed;
UIImageView *IconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"marker.png"]];
newAnnotation.leftCalloutAccessoryView = IconView;
newAnnotation.animatesDrop = YES;
newAnnotation.canShowCallout = YES;
return newAnnotation;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Got it to work thanks to a suggestion above. Also added in custom markers and a left icon to call out. I commented out the old coordinate code and the annotation red pins with left icon.
Hope this helps others who might've gone crazy searching for a solution as I have.
Stadiums.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Stadiums</key>
<array>
<dict>
<key>Title</key>
<string>Arsenal</string>
<key>Subtitle</key>
<string>The Gunners</string>
<key>Latitude</key>
<string>51.556899</string>
<key>Longitude</key>
<string>-0.106403</string>
</dict>
<dict>
<key>Title</key>
<string>Chelsea</string>
<key>Subtitle</key>
<string>The Blue Lions</string>
<key>Latitude</key>
<string>51.481314</string>
<key>Longitude</key>
<string>-0.190129</string>
</dict>
</array>
</dict>
</plist>
ViewController.m
#import "ViewController.h"
#import "Annotation.h"
#interface ViewController ()
#end
//Wimbledon Coordinates
#define WIMB_LATITUDE 51.434783;
#define WIMB_LONGITUDE -0.213428;
//Stadium Coordinates
#define ARSENAL_LATITUDE 51.556899;
#define ARSENAL_LONGITUDE -0.106403;
#define CHELSEA_LATITUDE 51.481314;
#define CHELSEA_LONGITUDE -0.190129;
//Span
#define THE_SPAN 0.10f;
#implementation ViewController
#synthesize myMapView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the region
MKCoordinateRegion myRegion;
//Center
CLLocationCoordinate2D center;
center.latitude = ARSENAL_LATITUDE;
center.longitude = ARSENAL_LONGITUDE;
//Span
MKCoordinateSpan span;
span.latitudeDelta = THE_SPAN;
span.longitudeDelta = THE_SPAN;
myRegion.center = center;
myRegion.span=span;
//Set our mapView
[myMapView setRegion:myRegion animated:YES];
/*
//Annotation
NSMutableArray * locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Annotation * myAnn;
//Arsenal Annotation
myAnn = [[Annotation alloc] init];
location.latitude = ARSENAL_LATITUDE;
location.longitude = ARSENAL_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Arsenal FC";
myAnn.subtitle = #"The Gunners";
//myAnn.image = [UIImage imageNamed:#"location.png"];
[locations addObject:myAnn];
//Chelsea Annotation
myAnn = [[Annotation alloc] init];
location.latitude = CHELSEA_LATITUDE;
location.longitude = CHELSEA_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Chelsea FC";
myAnn.subtitle = #"The Blue Lions";
[locations addObject:myAnn];
[self.myMapView addAnnotations:locations];
*/
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Stadiums" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"Stadiums"];
NSLog(#"read1");
for(int i = 0; i < [anns count]; i++) {
NSLog(#"read2");
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"Latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"Longitude"] floatValue];
NSLog(#"read3");
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 release];
}
}
//THIS CODE WORKS FOR CUSTOM ANNOTATIONS
- (MKAnnotationView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation
{
static NSString *AnnotationViewID = #"annotationViewID";
MKAnnotationView *annotationView = (MKAnnotationView *)[myMapView dequeueReusableAnnotationViewWithIdentifier:AnnotationViewID];
if (annotationView == nil)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationViewID];
}
//Custom Pin
annotationView.image = [UIImage imageNamed:#"toilets.png"];
//Custom Thumbnail (left side)
UIImageView *IconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"toilets.png"]];
annotationView.leftCalloutAccessoryView = IconView;
annotationView.canShowCallout = YES;
annotationView.annotation = annotation;
return annotationView;
/*
MKPinAnnotationView *newAnnotation = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"redpin"];
newAnnotation.pinColor = MKPinAnnotationColorRed;
UIImageView *ThumbView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"toilets.png"]];
newAnnotation.leftCalloutAccessoryView = ThumbView;
newAnnotation.animatesDrop = YES;
newAnnotation.canShowCallout = YES;
return newAnnotation;
*/
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Personally, I would use CoreData to store the annotations. However, if you are stuck on writing things to disk, I would make your annotation object implement the NSCoding protocol and use NSKeyedArchiver to save and load your objects.
+ (NSObject *)readArchiveFile:(NSString *)inFileName
{
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectoryPath, inFileName];
NSObject *returnObject = nil;
if( [fileMgr fileExistsAtPath:filePath] )
{
#try
{
returnObject = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
}
#catch (NSException *exception)
{
[FileUtils deleteFile:inFileName];
returnObject = nil;
}
}
return returnObject;
}
+ (void)archiveFile:(NSString *)inFileName inObject:(NSObject *)inObject
{
NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectoryPath, inFileName];
#try
{
BOOL didSucceed = [NSKeyedArchiver archiveRootObject:inObject toFile:filePath];
if( !didSucceed )
{
NSLog(#"File %# write operation %#", inFileName, didSucceed ? #"success" : #"error" );
}
}
#catch (NSException *exception)
{
NSLog(#"File %# write operation threw an exception:%#", filePath, exception.reason);
}
}
I have searched but i didn't found any solution to my problem. I want to change already added pin. And here is my code. I'm getting "Code Here" in console.
I have in .h
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView;
myViewController.m
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
userPins *myAnnot = (userPins *)annotationView.annotation;
[myAnnot setTitle:#"o21"];
NSLog(#"Code here!");
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"userPins";
if ([annotation isKindOfClass:[userPins class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.image = [UIImage imageNamed:#"mapMarker-blue.png"];
annotationView.calloutOffset = CGPointMake(0,0);
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = detailButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
In userPins.m i have. But i didn't do this with setTitle too.
- (void)setTitle:(NSString *)title {
if (_subtitle != subtitle) {
[_subtitle release];
_subtitle = [subtitle retain];
}
}
- (NSString *)title{
return #"okan";
}
Edit: #AnnaKarenina
I have tried all. But setTitle is not running. I'm going crazy! This is all of my code. And I'm not getting errors.
userpins.m
#import "userPins.h"
#implementation userPins
#synthesize fbid = _fbid;
#synthesize coordinate = _coordinate;
#synthesize title = _title;
#synthesize subtitle = _subtitle;
- (id)initWithfbid:(NSInteger*)fbid
coordinate:(CLLocationCoordinate2D)coordinate {
if ((self = [super init])) {
_fbid = fbid;
_coordinate = coordinate;
}
return self;
}
- (NSString *)subtitle{
return #"Anything";
}
- (NSString *)title{
return #"Something";
}
- (void)dealloc
{
[_title release];
[_subtitle release];
[super dealloc];
}
#end
userPins.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface userPins : NSObject <MKAnnotation> {
NSInteger *_fbid;
CLLocationCoordinate2D _coordinate;
NSString *_title;
NSString *_subtitle;
}
#property (nonatomic, readonly) NSInteger *fbid;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
- (id)initWithfbid:(NSInteger*)fbid coordinate:(CLLocationCoordinate2D)coordinate;
#end
viewController.m
#import "lociseViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJSON.h"
#import "userPins.h"
#implementation lociseViewController
#synthesize mapView = _mapView;
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Add new method above refreshTapped
- (void)plotPositions:(NSString *)responseString {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
NSDictionary * root = [responseString JSONValue];
NSArray *data = [root objectForKey:#"data"];
for (NSArray * row in data) {
NSInteger * fbid = (NSInteger *)[row objectAtIndex:0];
NSNumber * latitude = [row objectAtIndex:1];
NSNumber * longitude = [row objectAtIndex:2];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
userPins *annotation = [[[userPins alloc] initWithfbid:fbid coordinate:coordinate] autorelease];
[_mapView addAnnotation:annotation];
}
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
// 1
MKCoordinateRegion mapRegion = [_mapView region];
CLLocationCoordinate2D centerLocation = mapRegion.center;
// 2
NSString *jsonFile = [[NSBundle mainBundle] pathForResource:#"command" ofType:#"json"];
NSString *formatString = [NSString stringWithContentsOfFile:jsonFile encoding:NSUTF8StringEncoding error:nil];
NSString *json = [NSString stringWithFormat:formatString, centerLocation.latitude, centerLocation.longitude, 0.5*METERS_PER_MILE];
NSURL *url = [NSURL URLWithString:#"http://localhost/users.json"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request.requestMethod = #"POST";
//request.defaultResponseEncoding = NSISOLatin1StringEncoding;
//[request addRequestHeader:#"Content-Encoding" value:#"windows-1255"];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request appendPostData:[json dataUsingEncoding:NSUTF8StringEncoding]];
[request setDefaultResponseEncoding:NSUTF8StringEncoding];
// 5
[request setDelegate:self];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
[self plotPositions:responseString];
}];
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Error: %#", error.localizedDescription);
}];
// 6
[request startAsynchronous];
//CLLocationManager *locationManager;
//locationManager.delegate = self;
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
// 1
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 35.333070;
zoomLocation.longitude = 33.302875;
// 2
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 50*METERS_PER_MILE, 3*METERS_PER_MILE);
// 3
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
// 4
[_mapView setRegion:adjustedRegion animated:YES];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
userPins *myAnnot = (userPins *)annotationView.annotation;
[myAnnot setTitle:#"o21"];
NSLog(#"Title: %#", annotationView.annotation.subtitle);
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"userPins";
if ([annotation isKindOfClass:[userPins class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.image = [UIImage imageNamed:#"mapMarker-blue.png"];
annotationView.calloutOffset = CGPointMake(0,0);
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = detailButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
- (void)viewDidUnload
{
[self setMapView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[_mapView release];
[super dealloc];
}
#end
viewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface lociseViewController : UIViewController <MKMapViewDelegate> {
MKMapView *_mapView;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView;
#end
The setTitle: and title methods in userPins.m don't look right.
The setTitle: method is looking at and setting subtitle instead of title. The title method returns a constant string so even if setTitle: set the title ivar, the annotation will return the same constant string.
You could fix those methods or just declare title and subtitle as copy properties and #synthesize them in userPins.m (and release in dealloc). Then you don't need those methods at all (they will be implemented automatically).
By the way, when you add the annotation, be sure to set the title to something (even a single space) otherwise the callout will not display and didSelectAnnotationView will not be called.
A separate problem is a memory leak in viewForAnnotation. You should add an autorelease where you alloc+init annotationView.
Regarding The Updated Code:
In userPins.m, it still returns constant strings for subtitle and title thus hiding the changed ivars. Change it to:
- (id)initWithfbid:(NSInteger*)fbid
coordinate:(CLLocationCoordinate2D)coordinate {
if ((self = [super init])) {
_fbid = fbid;
_coordinate = coordinate;
//init the ivars here...
_title = #"Something";
_subtitle = #"Anything";
}
return self;
}
/* comment these methods out
- (NSString *)subtitle{
return #"Anything";
}
- (NSString *)title{
return #"Something";
}
*/
A new, unrelated problem is that fbid should probably be NSNumber * instead of NSInteger *. NSInteger is not an object and an NSNumber is what the JSON parser is most likely giving you.