1

I'm building an iPhone app and my label keeps grabbing the last item in my plist. The first portion of the app displays a map with annotations. The annotations -- latitude and longitude along with a title are all being pulled from the same plist as well.

Here is my code:

 #import "myMapViewController.h"
    #import "MapViewAnnotation.h"

    @interface myMapViewController ()
    @property NSArray *states;
    //@property NSString *nameString;
    //@property NSString *zipString;
    //@property NSString *subtitleString;
   //@property NSString *zipString;
    @end

    @implementation myMapViewController

    @synthesize mapView;



    - (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    //self.mapView.showsUserLocation = YES;  //showsUserLocation = YES;
    self.mapView.delegate = self;
    [self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated: YES];
    [self.mapView addAnnotations:[self createAnnotations]];

    //[self zoomToLocation];
}

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

- (NSMutableArray *)createAnnotations
{
    NSMutableArray *annotations = [[NSMutableArray alloc] init];

    NSString *path = [[NSBundle mainBundle]pathForResource:@"States" ofType:@"plist"];
    NSArray *rootLevel = [[NSMutableArray alloc]initWithContentsOfFile:path];

    int count = rootLevel.count;

    for (int i = 0; i < count; i++){

        NSDictionary *secondLevel = [rootLevel objectAtIndex:i];
        // NSArray *state = [secondLevel valueForKey:@"State"];
        NSArray *stores = [secondLevel valueForKey:@"Stores"];


        for (NSDictionary *row in stores) {
            NSString *latitude = [row objectForKey:@"Latitude"];
            NSString *longitude = [row objectForKey:@"Longitude"];
            NSString *title = [row objectForKey:@"Name"];

            NSString *nameString = [row objectForKey:@"Number"];
            NSString *zipString = [row objectForKey:@"Zip"];
            NSString *subtitle = [row objectForKey:@"Address1"];

            //Create coordinates from the latitude and longitude values
            CLLocationCoordinate2D coord;
            coord.latitude = latitude.doubleValue;
            coord.longitude = longitude.doubleValue;

            MapViewAnnotation *annotation = [[MapViewAnnotation alloc] initWithTitle:title AndCoordinate:coord AndZipString:zipString AndNameString:nameString AndSubSubtitle:subtitle];

            [annotations addObject:annotation];

            //set label


        }
    }
    return annotations;
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"showStoreDetails"])
    {



        MKAnnotationView *annotationView = sender;
        id<MKAnnotation> ann = annotationView.annotation;
       [segue.destinationViewController setNameString:ann.title];
       [segue.destinationViewController setZipString:ann.title];
        NSLog(@"log %@", annotationView.annotation.subtitle);

    }
}

- (MKAnnotationView *)mapView:(MKMapView *)sender viewForAnnotation:(id < MKAnnotation >)annotation
{
    static NSString *reuseId = @"StandardPin";

    MKPinAnnotationView *aView = (MKPinAnnotationView *)[sender
                                                         dequeueReusableAnnotationViewWithIdentifier:reuseId];

    if (annotation == mapView.userLocation){
        return nil; //default to blue dot
    }

    if (aView == nil)
    {
        aView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                reuseIdentifier:reuseId];
        aView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        aView.canShowCallout = YES;
        //aView.animatesDrop = YES;
    }

    aView.annotation = annotation;
    return aView;
}

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view
calloutAccessoryControlTapped:(UIControl *)control
{
    [self performSegueWithIdentifier:@"showStoreDetails" sender:view];
    NSLog(@"accessory button tapped for annotation %@", view.annotation.title);

}

- (void)mapView:(MKMapView *)mv didAddAnnotationViews:(NSArray *)views {
    for(MKAnnotationView *annotationView in views) {
        if(annotationView.annotation == mv.userLocation) {
            MKCoordinateRegion region;
            MKCoordinateSpan span;

            span.latitudeDelta=0.7;
            span.longitudeDelta=0.7;

            CLLocationCoordinate2D location=mv.userLocation.coordinate;

            region.span=span;
            region.center=location;

            [mv setRegion:region animated:TRUE];
            [mv regionThatFits:region];
        }
    }
}

@end
leflis
  • 21
  • 9
  • you mean it keeps showing only last data/record from the plist? – Kunal May 07 '14 at 15:12
  • 1
    Completely off-topic, but do you realize that your METERS_PER_MILE constant is wrong? It should be 1609.344. – David Berry May 07 '14 at 15:35
  • It's not clear at all what your question and/or problem is. Since there's no mention of a label in your code, what label is being set to the last value? – David Berry May 07 '14 at 15:40

4 Answers4

1

When you are iterating through your plist, you need an array to store your namestring instead of simple self.namestring NSString.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
1

It looks like at no point are you actually giving the MapViewAnnotation object the label for the location.

When you loop through the data from the plist the only thing that is happening to the label is it being set to self.nameString which will keep getting overwritten by each iteration of the loop.

Not sure exactly what your code is trying to do but it looks like you aren't doing enough with the name value to achieve what you want.

Lee McDonald
  • 155
  • 8
  • He sets the title (and then the annotation title) to the same value as nameString, so it looks like he's setting the pin title correctly. – David Berry May 07 '14 at 15:39
0

It is quite simple. Your inner loop does the job of assigning text to the label. These two loops goes through all the records and keeps assigning the name to the nameString removing the first recorded label

self.nameString = nameString;

And hence stores the last string only.

Hope this helps.

Thanks, Kunal

Kunal
  • 649
  • 4
  • 13
  • So what would you suggest to fix? – leflis May 07 '14 at 15:43
  • please tell me what exactly you want to achieve then only I can help you out! – Kunal May 07 '14 at 15:49
  • The label on my details page (from the map annotation being pressed) is only displaying the last item in my plist. However all of my annotations and titles are correct.. all from the same plist. Regardless of what annotation is pressed. – leflis May 07 '14 at 15:51
  • So you want only to display the name of the annotation which is being pressed.. correct? – Kunal May 07 '14 at 15:57
  • yes and eventually other items from the list along with a call and directions button. But kinda stuck on this for now. – leflis May 07 '14 at 16:00
  • you need to send store object to the screen you are navigating to and you will get a name directly from that object – Kunal May 07 '14 at 16:08
  • I thought I was with -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([[segue identifier] isEqualToString:@"showStoreDetails"]) { [segue.destinationViewController setNameString:self.nameString]; } } – leflis May 07 '14 at 19:23
0

The problem appears to be that in prepareForSegue, you are sending self.nameString to the destinationViewController but that variable contains only the last item read.

Notice that in calloutAccessoryControlTapped, you are calling performSegueWithIdentifier with the sender as view (ie. the view of the annotation selected) but in prepareForSegue, you are not using sender.

You should use sender in prepareForSegue instead of self.nameString.


See MKAnnotationView Push to View Controller when DetailDesclosure Button is Clicked for a detailed example.

The updated code in prepareForSegue might look like this:

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"showStoreDetails"])
    {
        MKAnnotationView *annotationView = sender;
        id<MKAnnotation> ann = annotationView.annotation;
        [segue.destinationViewController setNameString:ann.title];
    }
}
Community
  • 1
  • 1
  • This make perfect sense. But I am now having trouble getting more information to the details view. Where and how do I add the other items in my plist to pass to other labels. – leflis May 08 '14 at 12:59
  • In destinationViewController, instead of a setNameString method/property, you can have a method/property to accept the entire `MapViewAnnotation` object. Then you can pass it `ann` instead of just `ann.title`. –  May 08 '14 at 13:02
  • Please excuse me as I am learning all of this as I go. But I still can't wrap my head around what I need to do. How do I include all of my strings in the MapViewAnnotation object? – leflis May 08 '14 at 14:20
  • By "other items in the plist" I assumed you meant other details specific to each annotation. In the MapViewAnnotation class, you currently maybe have only the title and coordinate properties. You can add the additional info you want to keep with each annotation (address, phone#, hours, etc) and set this info when you create each annotation. See http://stackoverflow.com/questions/5939223/store-data-in-mkannotation for an example of this. –  May 08 '14 at 14:37
  • It's hard to tell what the problem might be from the words "still nothing". Can you please give more details like what NSLogs you see, what steps you do and what happens or what you see, etc? Have you stepped through the code in the debugger and checked whether each method is called properly, etc? Also please note you are passing ann.title when calling setZipString. With SO questions, it's better if you focus on one small problem at a time, close it if that problem is solved and ask another question for the next problem. –  May 08 '14 at 16:07
  • Anna. Thanks for being so patient with me. I have tried adding those methods propertied to the MapViewAnnotation like you mentioned and thought i could get those items to the destinationViewController. I know ann.title always passed the title property from the MapViewAnnotation. But it doesnt recognize anything else but ann.title.. ie: ann.zipString..doesnt work. nothing shows up in the label. – leflis May 08 '14 at 16:18
  • In prepareForSegue, when calling setZipString, the code is passing ann.title. Instead, it should pass ann.zip (assuming you created a zip property in MapViewAnnotation). You need to do some debugging which means confirming each part of the code actually does what you think it should do. As is, there are too many possibilities of things that could go wrong. –  May 08 '14 at 16:25