Annotation map pin title showing wrongly - ios

Annotation pin title shows same title for all pins. I have set label to show the title of each pin but I am getting same title for all labels. I did NSLog
of lbl.text and in NSLog it shows different title.
Why am I getting same title for all map pins.
-(void)maprequests
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// getting an NSString
NSString *emailid = [prefs stringForKey:#"email"];
NSString *deviceid = [Request UDID];
//NSString * walkGUID=[prefs stringForKey:#"walkguid"];
//NSLog(#"walkGUID:%#",walkGUID);
NSString * walkguid=[prefs stringForKey:#"walkguid"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://10.0.0.28/web/ws/get_poilist_walks.php?strEmailID=%#&strDeviceID=%#&strWalkGuid=%#",emailid,deviceid,walkguid]];
NSLog(#"%#",url);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//AFNetworking asynchronous url request
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:#"text/html"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
self.pointofintrests = [responseObject objectForKey:#"PointOfIntrests"];
NSIndexPath *indexpath;
NSDictionary *tempDictionary= [self.pointofintrests objectAtIndex:indexpath.row];
for (NSDictionary *dictionary in _pointofintrests)
{
MKCoordinateRegion region = { {0.0, 0.0 }, { 0.0, 0.0 } };
NSString * latitude= [dictionary objectForKey:#"Latitude"];
NSString * longitude =[dictionary objectForKey:#"Longitude"];
double strlatitude = [latitude doubleValue];
double strlongitude = [longitude doubleValue];
region.center.latitude =strlatitude;
region.center.longitude = strlongitude;
region.span.longitudeDelta = 0.01f;
region.span.latitudeDelta = 0.01f;
[_mapview setRegion:region animated:YES];
[_mapview setDelegate:self];
DisplayMap *ann = [[DisplayMap alloc] init];
ann.coordinate = region.center;
[_mapview addAnnotation:ann];
}
}
failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Request Failed: %#, %#", error, error.userInfo);
}];
[operation start];
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *pinView = nil;
UILabel *label;
if(annotation != mapView.userLocation)
{
static NSString *defaultPinID = #"com.invasivecode.pin";
pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if ( pinView == nil ) pinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:defaultPinID];
if(![annotation isKindOfClass:[DisplayMap class]])
return nil;
DisplayMap *a = (DisplayMap *)annotation;
pinView.image=[UIImage imageNamed:#"push_pin#2x"];
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 250, 30)];
lbl.backgroundColor = [UIColor clearColor];
lbl.textColor = [UIColor whiteColor];
lbl.alpha = 0.5;
lbl.tag = 42;
for (int i=0; i<_pointofintrests.count; i++)
{
lbl.text = [[_pointofintrests valueForKey:#"title"] objectAtIndex:i];
}
[pinView addSubview:lbl];
[_mapview selectAnnotation:pinView animated:YES];
pinView.canShowCallout = YES;
pinView.animatesDrop = NO;
}
else
{
}
return pinView;
}

Your problem is you are looping over _pointofinterests
for (int i=0; i<_pointofintrests.count; i++)
{
lbl.text = [[_pointofintrests valueForKey:#"title"] objectAtIndex:i];
}
This isn't doing what you think. Its assigning the last title in the array to every pin.
Because the viewForAnnotation delegate method gives you the annotation that will display, I tend to keep an array of annotations to save the index, and therefore you can access your data correctly.
When you call [_mapview addAnnotation:ann]; also save ann into an array.
You should then be able todo something like:
[[_pointofintrests valueForKey:#"title"] objectAtIndex:[annArray indexOfObject:annotation]]
As a side note, if _pointofinterests is an NSDictionary I would use objectForKey instead.

The main reason you are getting the same label text on all pins is, as already pointed out in another answer, that for every pin, lbl.text is always set to the title of the last object in _pointofintrests.
The solution I would prefer however is to set the annotation's title and use that to set the label's text.
When creating the annotation and before calling addAnnotation, set its title property:
DisplayMap *ann = [[DisplayMap alloc] init];
ann.coordinate = region.center;
ann.title = [dictionary objectForKey:#"title"]; //<-- add this line
[_mapview addAnnotation:ann];
In viewForAnnotation, instead of the for loop, you can simply set the label's text to the annotation's title (no searching of arrays or looping):
//for (int i=0; i<_pointofintrests.count; i++)
//{
// lbl.text = [[_pointofintrests valueForKey:#"title"] objectAtIndex:i];
//}
lbl.text = annotation.title;
That technically fixes the problem of the same text appearing on all pins.
However, there are some other issues with the code in viewForAnnotation which will become apparent after the above fix:
The UILabel is being added to the annotation view even if the annotation view was dequeued (meaning a previously created view is being re-used). That dequeued view will already have a UILabel in it and the existing code will add another one on top. After panning and zooming the map for a while, you will notice overlapping labels for each pin.
The UILabel should only be added when a new view is created (when the dequeue returns nil and you alloc+init a new one).
Another problem is that since you are using a custom image for your annotations, you should create a plain MKAnnotationView instead of an MKPinAnnotationView. The MKPinAnnotationView class is designed for showing the standard, built-in pin image and although it still has an image property, it tends to sometimes ignore that and revert to showing its standard pin image.
Another problem is this line:
[_mapview selectAnnotation:pinView animated:YES];
The map view can only have one annotation "selected" at a time so if you were thinking that calling this in viewForAnnotation would show the callout for all the annotations at once, then that is mistaken.
The selectAnnotation method requires the annotation as the first parameter. The pinView is not the annotation -- it is the annotation's view (they are not the same thing). The compiler must be complaining about this line and at run-time, the system is probably showing an error in the console saying something like "Trying to select an annotation which has not been added". So technically, the line should be [_mapview selectAnnotation:annotation animated:YES];
Regardless, do not call selectAnnotation in the viewForAnnotation delegate method. Doing so may lead to recursion and a stack overflow (and an EXC_BAD_ACCESS) because the select causes the annotation's view to be updated which causes viewForAnnotation to be called, etc. Instead, call it in the didAddAnnotationViews delegate method for the one annotation you want to show the callout for.
Your revised viewForAnnotation delegate method might look like this:
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if(! [annotation isKindOfClass:[DisplayMap class]])
{
return nil;
}
static NSString *defaultPinID = #"MyPin";
int lblTag = 42;
MKAnnotationView *pinView = [mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if (pinView == nil)
{
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID];
pinView.image = [UIImage imageNamed:#"push_pin#2x"];
pinView.canShowCallout = YES;
//Create and add the label to the view ONCE when creating the view...
UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 250, 30)];
lbl.backgroundColor = [UIColor clearColor];
lbl.textColor = [UIColor whiteColor];
lbl.alpha = 0.5;
lbl.tag = lblTag;
[pinView addSubview:lbl];
}
else
{
//If we are re-using a dequeued view
//update its annotation reference...
//(otherwise view will still be pointing to its previous annotation)
pinView.annotation = annotation;
}
//At this point, we have a new or dequeued view
//pointing to the current annotation.
//Now update the label that will already be there
//with the current annotation's title...
UILabel *lbl = (UILabel *)[pinView viewWithTag:lblTag];
lbl.text = annotation.title;
return pinView;
}
Remember you'll need to set the annotation's title as shown at the top of the answer as well.
There are also some things in the for loop in the maprequests method that I want to point out:
It's not necessary to create and set a region just to set the annotation's coordinate. Calling setRegion in the loop is pointless and inefficient since the user will only end up seeing the map positioned at the last annotation anyway. To set an annotation coordinate, just do:
ann.coordinate = CLLocationCoordinate2DMake(strlatitude, strlongitude);
You can call setRegion (once) after the for loop (or just call [_mapview showAnnotations:_mapview.annotations animated:YES]; so you don't have to yourself calculate the region that shows all the annotations).
You don't need to set the map view's delegate inside the for loop repeatedly. Set the delegate (once) before the for loop (though it should already have been set long before this method is called).

Related

Unwanted annotation in map ios

I am trying to annotate some locations in map using MapBox. and its done.
But the problem is some other annotations are seen in map without any reference, i.e we cant click on it and when i zoom in/zoom out it disappears.
Am only try to create two annotations including user location.
How this happen?
is it because any reusing of annotation?
Some code snippets used:
1) creating annotation in loop
MGLPointAnnotation *point = [[MGLPointAnnotation alloc] init];
point.coordinate = coordinate;
point.title = location;
NSString *altString =[NSString stringWithFormat:#"%#",mIsNSDictionary(response)?[response objectForKey:kKeyAlt]:response];
NSString *str = [NSString stringWithFormat:kKeyLocationLatLonNAltInBaidu,coordinate.latitude,coordinate.longitude,[altString floatValue]];
point.subtitle = str;
[self.arrayAnnotations addObject:point];
[self.mapView addAnnotation:point];
Note: self.arrayAnnotations contains only 2 points
2) Delegate method for annotation
-(MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation {
if([annotation isKindOfClass:[MGLPointAnnotation class]] && [self.arrayAnnotations containsObject:annotation]) {
NSString *reuseIdentifier = [self makeIdentifierString:annotation];
MGLAnnotationImage *annotationImage = [mapView dequeueReusableAnnotationImageWithIdentifier:reuseIdentifier];
if (!annotationImage) {
annotationImage = [MGLAnnotationImage annotationImageWithImage:[UIImage imageNamed:kImageNameMarker] reuseIdentifier:reuseIdentifier];
}
return annotationImage;
}
return nil;
}

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;
}

Map view shows my location and destination but doesn't show directions between them

When I initialize the map, it shows both my location and the destination (forward geocoded from a string) but it does not draw directions between them.
Here is my code :
#import "EventDetailMapViewController.h"
#interface EventDetailMapViewController ()
#property (nonatomic,strong) MKMapItem *destination;
#end
#implementation EventDetailMapViewController
CLPlacemark *thePlacemark;
MKRoute *routeDetails;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_mapView.showsUserLocation = YES;
self.navigationController.toolbarHidden = NO;
_mapView.delegate = self;
[self getRoute];
}
- (void)addAnnotation:(CLPlacemark *)placemark {
MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
point.coordinate = CLLocationCoordinate2DMake(placemark.location.coordinate.latitude, placemark.location.coordinate.longitude);
point.title = [placemark.addressDictionary objectForKey:#"Street"];
point.subtitle = [placemark.addressDictionary objectForKey:#"City"];
[self.mapView addAnnotation:point];
}
-(void)showRoute:(MKDirectionsResponse *)response{
for (MKRoute *route in response.routes)
{
[_mapView
addOverlay:route.polyline level:MKOverlayLevelAboveRoads];
for (MKRouteStep *step in route.steps){
NSLog(#"%#",step.instructions);
}
}
}
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// Handle any custom annotations.
if ([annotation isKindOfClass:[MKPointAnnotation class]]) {
// Try to dequeue an existing pin view first.
MKPinAnnotationView *pinView = (MKPinAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier:#"CustomPinAnnotationView"];
if (!pinView)
{
// If an existing pin view was not available, create one.
pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"CustomPinAnnotationView"];
pinView.canShowCallout = YES;
} else {
pinView.annotation = annotation;
}
return pinView;
}
return nil;
}
-(void)getRoute {
[self getLocationFromString];
MKDirectionsRequest *directionsRequest = [[MKDirectionsRequest alloc] init];
MKPlacemark *placemark = [[MKPlacemark alloc] initWithPlacemark:thePlacemark];
[directionsRequest setSource:[MKMapItem mapItemForCurrentLocation]];
[directionsRequest setDestination:[[MKMapItem alloc] initWithPlacemark:placemark]];
directionsRequest.transportType = MKDirectionsTransportTypeAutomobile;
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
if (error) {
NSLog(#"Error %#", error.description);
} else {
routeDetails = response.routes.lastObject;
[self.mapView addOverlay:routeDetails.polyline];
}
}];
}
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
MKPolylineRenderer * routeLineRenderer = [[MKPolylineRenderer alloc] initWithPolyline:routeDetails.polyline];
routeLineRenderer.strokeColor = [UIColor redColor];
routeLineRenderer.lineWidth = 5;
return routeLineRenderer;
}
- (IBAction)changeMapType:(id)sender {
if (_mapView.mapType == MKMapTypeStandard)
_mapView.mapType = MKMapTypeSatellite;
else
_mapView.mapType = MKMapTypeStandard;
}
-(void)getLocationFromString{
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:self.agendaEntry.address completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
thePlacemark = [placemarks lastObject];
float spanX = 1.00725;
float spanY = 1.00725;
MKCoordinateRegion region;
region.center.latitude = thePlacemark.location.coordinate.latitude;
region.center.longitude = thePlacemark.location.coordinate.longitude;
region.span = MKCoordinateSpanMake(spanX, spanY);
[self.mapView setRegion:region animated:YES];
[self addAnnotation:thePlacemark];
}
}];
}
#end
I'm relatively new to this so some of this is probably wrong. What I want is when I push a button (in another view) the "agendaEntry" gets passed on the segue. It contains a string with an address, this address gets forward-geocoded and the map displays directions/routes from the user's location to the address in that string.
I don't know how to make it show driving/walking directions or to make it show multiple routes. But for starters, it would be great if it would work.
The reason it doesn't draw directions is because the directions request is being made before the destination placemark (thePlacemark) is actually set by the geocodeAddressString completion handler block.
Note what the documentation says about geocodeAddressString:completionHandler::
This method submits the specified location data to the geocoding server asynchronously and returns.
So even though getLocationFromString (which calls geocodeAddressString) is called before the directions request, execution returns and continues in getRoute immediately after the geocodeAddressString is initiated (but not completed).
Therefore, thePlacemark is still nil when getRoute sets up directionsRequest and you're probably getting an error logged such as "directions not available".
To account for this, you can move the call to the directions-request-set-up code to inside the geocodeAddressString completion handler block (after the add-annotation code).
There are three things to change for this:
In viewDidLoad, do [self getLocationFromString]; instead of [self getRoute];.
In getRoute, remove the call to getLocationFromString.
In getLocationFromString, in the completion handler block, after [self addAnnotation:thePlacemark];, do [self getRoute];.
Regarding showing multiple routes, first you'll need to actually request alternate routes (default is NO):
directionsRequest.requestsAlternateRoutes = YES;
The other part of the problem you may have experienced is due to this line in rendererForOverlay:
MKPolylineRenderer * routeLineRenderer = [[MKPolylineRenderer alloc]
initWithPolyline:routeDetails.polyline];
It's creating a polyline renderer using an external instance variable instead of the overlay parameter provided to the delegate method. This basically means the delegate method will only work for that one specific overlay (routeDetails.polyline) and since there's no control over when exactly the delegate method will be called by the map view, you can't reliably set routeDetails.polyline from outside the delegate method to be sure it points to the right overlay. Each alternate route is a separate polyline and the map view will make separate calls to rendererForOverlay for each one.
Instead, create the polyline renderer using the overlay parameter (and first check if overlay is an MKPolyline):
-(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay {
if ([overlay isKindOfClass:[MKPolyline class]])
{
MKPolylineRenderer * routeLineRenderer =
[[MKPolylineRenderer alloc] initWithPolyline:overlay];
routeLineRenderer.strokeColor = [UIColor redColor];
routeLineRenderer.lineWidth = 5;
return routeLineRenderer;
}
return nil;
}
Then in getRoute, instead of this:
routeDetails = response.routes.lastObject;
[self.mapView addOverlay:routeDetails.polyline];
call the showRoute method you already have which adds all the routes:
[self showRoute:response];

Create a UIAlertView once map pin is selected

I've been looking around for a solution to the custom callout on MKMapview and I don't want to add images and text to the left or right accessory view. I think I would rather create a UIAlertView that brings up all of the information that map pin that was selected.
I've gathered the lat and long on the map pin from JSON data and that JSON data also contains other info such as: phone number, website link, and so on.
How would I accomplish getting all of that information into a UIAlertView that is called upon pressing the map pin?
Here's some sample code:
self.result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if(error == nil)
self.mapLocations = result;
NSLog(#"%#", [result allKeys]);
for (NSDictionary *location in self.mapLocations[#"merchants"]) {
for (NSDictionary *locations in location[#"branches"]) {
NSLog(#"%#",locations);
CLLocationCoordinate2D annotationCoordinate =
CLLocationCoordinate2DMake([locations[#"latitude"] doubleValue],
[locations[#"longitude"] doubleValue]);
Annotation *annotation = [[Annotation alloc] init];
annotation.coordinate = annotationCoordinate;
annotation.title = location[#"name"];
annotation.subtitle = locations[#"street"];
[self.mapView addAnnotation:annotation];
and here is my MKAnnotationView Method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annoPin"];
MKAnnotationView *view = [self.mapView dequeueReusableAnnotationViewWithIdentifier:#"annoView"];
if(!view) {
view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annoView"];
}
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:nil action:#selector(showDetails :) forControlEvents:UIControlEventTouchUpInside];
pinView.animatesDrop = YES;
view.rightCalloutAccessoryView = rightButton;
view.image = [UIImage imageNamed:#"check.png"];
view.enabled = YES;
view.canShowCallout = YES;
return view;
}
Help!
If you want to display the UIAlertView after the disclosure button is pressed you should take out the line about assigning an action to the button and implement – mapView:annotationView:calloutAccessoryControlTapped:. So long as your class is set to be the map's delegate then that function will be called and you can create your UIAlertView from there. On of the parameters to that function is the annotationView from which you can get the MKAnnotation and all the details should be attached to that.

New PList Retrieval Code Prevents MKMapView Delegate Method From Being Called (iOS)

I added a plist database to store information for annotations in a MKMapView. Once I implemented the code to grab the information, my delegate methods are no longer being called.
The code I added was:
- (void)viewDidLoad
{
NSMutableArray *annotations = [[NSMutableArray alloc]init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"MillersStores" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *anns = [dict objectForKey:#"Root"];
for(int i = 0; i < [anns count]; i++) {
float realLatitude = [[[anns objectAtIndex:i] objectForKey:#"Latitude"] floatValue];
float realLongitude = [[[anns objectAtIndex:i] objectForKey:#"Longitude"] floatValue];
MillersLocations *myAnnotation = [[MillersLocations 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:#"Address"];
[mapView addAnnotation:myAnnotation];
[annotations addObject:myAnnotation];
[myAnnotation release];
}
}
And this is one of delegate method that's no longer being called is:
- (MKAnnotationView *) mapView:(MKMapView *) mapView viewForAnnotation:(id ) annotation {
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
MKPinAnnotationView *pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil] autorelease];
pinView.pinColor = MKPinAnnotationColorRed;
pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
UIImageView *leftIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"MillersAnnotation.png"]];
pinView.leftCalloutAccessoryView = leftIconView;
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
pinView.rightCalloutAccessoryView = rightButton;
return pinView;
}
I have another delegate method in there that also zooms into the User's Location that's not being called either, as well as the calloutAccessoryControlTapped method that's no longer being called.
I know it has something to do with the new code, but I'm confused as to how to even debug this because it's not giving me errors and I can't log it because the entire methods aren't even being called. When I get rid of the new code, the old code works fine...What is it in the new code that negates the old code?
It sounds like the map view's delegate property is not set.
Did the old code contain this line:
mapView.delegate = self;
Add that to viewDidLoad or, in IB, connect the map view's delegate outlet to File's Owner.

Resources