Updating Detail View with Annotation Info

Discussion in 'iOS Programming' started by RagingGoat, May 7, 2013.

  1. RagingGoat macrumors 6502

    Joined:
    Jun 21, 2010
    #1
    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
    
     
  2. TheWatchfulOne, May 7, 2013
    Last edited: May 7, 2013

    TheWatchfulOne macrumors 6502

    TheWatchfulOne

    Joined:
    Jun 19, 2009
    #2
    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.
     
  3. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #3
    Take a look at where you're setting marketAnnotation. Does it change between there and this line?:
    Also, where/how is it declared?
     
  4. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #4
    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.
     
  5. TheWatchfulOne, May 7, 2013
    Last edited: May 7, 2013

    TheWatchfulOne macrumors 6502

    TheWatchfulOne

    Joined:
    Jun 19, 2009
    #5

    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.
     
  6. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #6
    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.
     

    Attached Files:

  7. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #7
    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?
     
  8. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #8
    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];
    }
    
     
  9. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #9
    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];
    }
    
     
  10. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #10
    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. :)
     
  11. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #11
    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.
     
  12. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #12
    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.
     
  13. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #13
    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.
     
  14. RagingGoat thread starter macrumors 6502

    Joined:
    Jun 21, 2010
    #14
    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!
     
  15. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #15
    Yay! Good for you!

    Thanks for the thanks.
     

Share This Page