Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

RagingGoat

macrumors 6502
Original poster
Jun 21, 2010
308
15
I have several annotations on my map view and I have a detail view that opens when you tap the button on the annotation. The detail view has a label for the title from the annotation and a label for the subtitle form the annotation. I'm trying to have the title and subtitle labels show the corresponding text from the annotation that gets selected. There are two problems I'm having with it. One, the labels aren't even showing up. Two, I inserted an NSLog to make sure the correct text gets sent to the label but no matter which annotation is selected, the title and subtitle are always from the last annotation added.

Here is some code:

AnnotationDetailView.h
Code:
//
//  AnnotationDetailView.h

#import <UIKit/UIKit.h>
#import "RSFM.h"

@interface AnnotationDetailView : UIViewController <CLLocationManagerDelegate, MKMapViewDelegate, MKAnnotation>
{
    
}
@property UILabel *ti;
@property UILabel *sub;

@end

AnnotationDetailView.m
Code:
//
//  AnnotationDetailView.m

#import "AnnotationDetailView.h"

@interface AnnotationDetailView ()

@end

@implementation AnnotationDetailView

@synthesize ti, sub;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    ti = [[UILabel alloc]initWithFrame:CGRectMake(20, 20, 280, 21)];
    sub = [[UILabel alloc]initWithFrame:CGRectMake(20, 68, 280, 21)];
    [self.view addSubview:ti];
    [self.view addSubview:sub];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

RSFM.m
Code:
//
//  RSFM.m
//  KFBNewsroom
//
//  Created by Adam Rayborn on 5/3/13.
//  Copyright (c) 2013 com.kfb. All rights reserved.
//


#import "RSFM.h"
#import "AnnotationDetailView.h"

@interface RSFM ()

@end

@implementation RSFM
{
    
}

@synthesize centerCoordinate, coordinate, title, subtitle;

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    
    if (self)
    {
        self.title = NSLocalizedString(@"Farm Markets", @"Farm Markets");
        // Create location manager object
        locationManager = [[CLLocationManager alloc]init];
        
        [locationManager setDelegate:self];
       
        [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
    }
    
    return self;
}
/*
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
{
    centerCoordinate = CLLocationCoordinate2DMake(37.7885, 85.3279);
}
*/
- (void)findLocation
{
    [locationManager startUpdatingLocation];
    [activityIndicator startAnimating];
    [locationManager stopUpdatingLocation];
}

- (void)foundLocation:(CLLocation *)loc
{
    CLLocationCoordinate2D coord = [loc coordinate];
    
    // Zoom the region to this location
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coord, 700000, 700000);
    [worldView setRegion:region animated:YES];
    
    [activityIndicator stopAnimating];
    [locationManager stopUpdatingLocation];
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"%@", newLocation);
    
    NSTimeInterval t = [[newLocation timestamp]timeIntervalSinceNow];
    
    if (t < -180)
    {
        return;
    }
    
    [self foundLocation:newLocation];
}

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
    NSLog(@"Could not find location: %@", error);
}

- (void)dealloc
{
    // Tell the location manager to stop sending us messages
    [locationManager setDelegate:nil];
}

- (MKAnnotationView*)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    // If it's the user location, return nil
    if ([annotation isKindOfClass:[MKUserLocation class]])
        return nil;
    
    // Try to dequeue an existing pin view first
    static NSString *annotationIdentifier = @"AnnotationIdentifier";
    MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
    pinView.animatesDrop = YES;
    pinView.canShowCallout = YES;
    
    UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    [rightButton setTitle:annotation.title forState:UIControlStateNormal];
    // [rightButton addTarget:self action:@selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
    pinView.rightCalloutAccessoryView = rightButton;
    
    return pinView;
}

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    AnnotationDetailView *detail = [[AnnotationDetailView alloc] initWithNibName:nil bundle:nil];
    detail.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    NSLog(@"%@", marketAnnotation.title);
    detail.ti.text = marketAnnotation.title;
    detail.sub.text = marketAnnotation.subtitle;
    [self.navigationController pushViewController:detail animated:YES];
     
    /*
    MKPlacemark *placemark = [[MKPlacemark alloc]initWithCoordinate:location addressDictionary:nil];
    MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placemark];
    [mapItem openInMapsWithLaunchOptions:nil];
     */
}

- (void)viewDidLoad
{
    [locationManager startUpdatingLocation];
    [worldView setShowsUserLocation:YES];
    [locationManager stopUpdatingLocation];
    
    NSMutableArray *marketLocations = [[NSMutableArray alloc]init];
    
    NSMutableArray *lat = [[NSMutableArray alloc]initWithObjects:@"37.7867266", @"37.0703517", @"37.1610806", @"37.318367", @"37.3559204", @"37.4154066", @"37.4757622", @"37.7450252", @"37.6318978", @"37.0716803", nil];
    
    NSMutableArray *lon = [[NSMutableArray alloc]initWithObjects:@"-87.608209", @"-88.1237899", @"-87.9148629", @"-87.5074402", @"-87.5448032", @"-87.8003148", @"-87.9515986", @"-87.9061638", @"-87.1148574", @"-87.3008418", nil];
    
    NSMutableArray *title1 = [[NSMutableArray alloc]initWithObjects:@"Cates Farm", @"Broadbent B & B Foods", @"Cayce's Pumpkin Patch", @"Metcalfe Landscaping", @"Brumfield Farm Market", @"Dogwood Valley Farm", @"Country Fresh Meats & Farmers Market", @"Jim David Meats", @"Trunnell's Farm Market", @"Lovell's Orchard & Farm Market", nil];
    
    NSMutableArray *subtitle1 = [[NSMutableArray alloc]initWithObjects:@"Hwy 425 Henderson, KY 42420", @"257 Mary Blue Road Kuttawa, KY 42055", @"153 Farmersville Road Princeton, KY 42445", @"410 Princeton Road Madisonville, KY 42431", @"3320 Nebo Road Madisonville, KY 42431", @"4551 State Route 109N Clay, KY 42404", @"9355 US Hwy 60 W Sturgis, KY 42459",@"350 T. Frank Wathen Rd. Uniontown, KY 42461", @"9255 Hwy 431 Utica, KY 42376", @"22850 Coal Creek Road Hopkinsville, KY 42240", nil];
    
    // CLLocationCoordinate2D location;
    // MKPointAnnotation *marketAnnotation;
    
    for (int x = 0; x < [lat count]; x++)
    {
        marketAnnotation = [[MKPointAnnotation alloc]init];
        location.latitude = [[lat objectAtIndex:x]floatValue];
        location.longitude = [[lon objectAtIndex:x]floatValue];
        marketAnnotation.coordinate = location;
        marketAnnotation.title = [title1 objectAtIndex:x];
        marketAnnotation.subtitle = [subtitle1 objectAtIndex:x];
        [marketLocations addObject:marketAnnotation];
    }
    
    [worldView addAnnotations:marketLocations];
    
    /*
    MKPointAnnotation *point = [[MKPointAnnotation alloc] init];
    point.coordinate = CLLocationCoordinate2DMake(37.7867266, -87.608209);
    point.title = @"Cates Farm";
    point.subtitle = @"Hwy 425 Henderson, KY 42420";
    [worldView addAnnotation:point];
     */
}

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
    CLLocationCoordinate2D loc = [userLocation coordinate];
    MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(loc, 700000, 700000);
    [worldView setRegion:region animated:YES];
    [locationManager stopUpdatingLocation];
    locationManager.delegate = nil;
}
 
- (IBAction)selectSegmentControl
{
    int segmentTouched = [mapVarieties selectedSegmentIndex];
    NSString *segmentName = [mapVarieties titleForSegmentAtIndex:segmentTouched];
    if ([segmentName isEqualToString:@"Street"])
    {
        [worldView setMapType:MKMapTypeStandard];
    }
    if ([segmentName isEqualToString:@"Satellite"])
    {
        [worldView setMapType:MKMapTypeSatellite];
    }
    if ([segmentName isEqualToString:@"Hybrid"])
    {
        [worldView setMapType:MKMapTypeHybrid];
    }
}

@end
 
I have several annotations on my map view and I have a detail view that opens when you tap the button on the annotation. The detail view has a label for the title from the annotation and a label for the subtitle form the annotation. I'm trying to have the title and subtitle labels show the corresponding text from the annotation that gets selected. There are two problems I'm having with it. One, the labels aren't even showing up. Two, I inserted an NSLog to make sure the correct text gets sent to the label but no matter which annotation is selected, the title and subtitle are always from the last annotation added.

Looking through your code, I did not see this delegate method implented:

Code:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view

Edit: Actually, after thinking about it, for what you are doing you may not need to implement that method. But I'll leave it in this post for informational purposes.

What I say below still applies:

Also, when an annotation is selected, it is added to an array which is accessed via a property on MKMapView call selectedAnnotations. Normally, you only have one annotation selected at a time, so you would access it this way:

Code:
myAnnotationView = [myMapview.selectedAnnotations objectAtIndex: 0];

I did not see anything like that in your code either.
 
Last edited:
I know why it's giving me the last annotation. That's because the of my loop so it's giving me whatever is the last one. I'm just not sure how to make it give me whatever the title is for the selected annotation and make it show up as a label on my detail view.
 
I know why it's giving me the last annotation. That's because the of my loop so it's giving me whatever is the last one. I'm just not sure how to make it give me whatever the title is for the selected annotation and make it show up as a label on my detail view.


Code:
myAnnotationView = [myMapview.selectedAnnotations objectAtIndex: 0];

Where "myAnnotation" is an instance of whichever class you are using for annotation views.

And then:

Code:
myAnnotation.title

For further information, check out the Apple documentation on the selctedAnnotations property.
 
Last edited:
And just to clarify what I'm wanting, here is an image. I'm wanting something like this with the title, address, and get directions buttons. I don't really need the satellite view like Apple has here.
 

Attachments

  • photo.png
    photo.png
    485.2 KB · Views: 145
Have you tried any of what TheWatchfulOne has suggested? I guess I'm not clear on where you are currently in solving your problem. Care to give us a progress report?
 
I think I've decided against having a detail view. I think I'm just going to open that pin in Maps to allow the user to get directions. I still have to figure out how to get the selected annotation and not the last one added so I'm going to try to use the suggestions given.

I'm using this to do it.

Code:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{   
    MKPlacemark *placemark = [[MKPlacemark alloc]initWithCoordinate:location addressDictionary:nil];
    MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placemark];
    [mapItem openInMapsWithLaunchOptions:nil];
}
 
A quick update: I got the coordinates of the selected annotation to open in the maps app like this. I may go back and attempt to do the detail view but for right now, I'm happy.

Code:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    id<MKAnnotation> ann = [mapView.selectedAnnotations objectAtIndex:0];
    CLLocationCoordinate2D coord = ann.coordinate;
    MKPlacemark *placemark = [[MKPlacemark alloc]initWithCoordinate:coord addressDictionary:nil];
    MKMapItem *mapItem = [[MKMapItem alloc]initWithPlacemark:placemark];
    [mapItem openInMapsWithLaunchOptions:nil];
}
 
I think I've decided against having a detail view. I think I'm just going to open that pin in Maps to allow the user to get directions...

As an end user that is looking at annotations on a map already, I think I would find it strange that when I requested details on an annotation I would be taken to a separate app (Maps) rather than getting the details in-app. But maybe that's just me. :)
 
Code:
myAnnotationView = [myMapview.selectedAnnotations objectAtIndex: 0];

Where "myAnnotation" is an instance of whichever class you are using for annotation views.

And then:

Code:
myAnnotation.title

For further information, check out the Apple documentation on the selctedAnnotations property.

There is a better, simpler, cleaner way than that. The calloutAccessoryTapped method (or whatever the exact name is) gets the annotation view passed in as a parameter. So, for that matter, does the method that's called when the user selects an annotation view.

The MKAnnotationView has a property annotation which is the annotation object associated with the view.

The OP should interrogate the annotation view for the annotation in question. It will always be correct, even if your app supports selecting multiple annotations (or whatever.) Much cleaner.
 
As an end user that is looking at annotations on a map already, I think I would find it strange that when I requested details on an annotation I would be taken to a separate app (Maps) rather than getting the details in-app. But maybe that's just me. :)

After thinking about it more, I agree. The experience will be much better so I'm going to go back to my original plan of taking the user to a detail view with the title, subtitle (address), a website (if any), and phone number. There will also be a Get Directions button to take the user to the maps app to get directions. Now, if I could just get the title and subtitle to go over to the detail view.
 
Now, if I could just get the title and subtitle to go over to the detail view.

Three things:
  1. Duncan C is steering you in the right direction. The view parameter in your mapView:annotationView:calloutAccessoryControlTapped: is key. It's of type MKAnnotationView and has some important properties.
  2. Rather than setting the individual properties (ti, sub) before presenting your detail view, I would just "pass" the entire annotation. Then have the detail view pull the individual pieces out as needed. This way, you can add things to the annotation and not have to worry about setting up properties and setting their values before presenting the detail view. The detail view takes care of all of that.
  3. You should maybe have a look at the MapCallouts sample app from Apple.
 
I just want to let everyone know that I've gotten my detail view working with all of the info I was wanting. The only thing I have left to add is a get directions button. Thanks for all of the help!
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.