iOS trouble accessing property of MKAnnotation - ios

I can't seem to access a custom MKAnnotation property inside of mapView:viewForAnnotation delegate method. As I understand it, the method takes annotations as values. I'm trying to target the properties of each annotation that gets passed in.
Code
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
//This actually returns a proper coordinate
NSLog(#"%f", annotation.coordinate.latitude);
//This gives me an error: Property 'annotationMarker' not found on object of type '__strong id<MKAnnotation>'
NSLog(#"%#", annotation.annotationMarker);
MKAnnotationView *testPin = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
testPin.image = [UIImage imageNamed:[[testArray objectAtIndex:1]annotationMarker]];
return testPin;
}
I thought maybe the custom property wasn't set correctly, but I'm able to log the property values for these annotations in other parts of the code.
Am I making some kind of syntax error? Does this delegate method strip out custom properties somehow?

You need to cast to your custom MKAnnotation-conformant class, e.g.
CustomAnnotation *customAnnotation = (CustomAnnotation *)annotation;
NSLog(#"%#", customAnnotation.annotationMarker);

Related

MKAnnotationView is losing reference to MKAnnotation

In an app I'm working on, users are directed to push a button to drop MKAnnotations onto the map. They will drop 2 or 3 pins, each of which is saved to an #property when the pin is added in didAddAnnotationViews because I need a reference to it later, and I need to know which pin it is - pin 1, 2, or 3 (the order in which they are dropped).
I'm using a custom MKAnnotation and MKAnnotationView class to add a few NSStrings to each annotation, I'm not sure if that's important or not.
I'm creating 3 properties like this:
#property (nonatomic, strong) CustomAnnotationView *ann1;
#property (nonatomic, strong) CustomAnnotationView *ann2;
#property (nonatomic, strong) CustomAnnotationView *ann3;
Here is my didAddAnnotationViews:
- (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views
{
for(MKAnnotationView *view in views)
{
if(![view.annotation isKindOfClass:[MKUserLocation class]])
{
CustomAnnotationView *newAnnView = (CustomAnnotationView*)view;
if(newAnnView.type == CustomType1)
{
ann1 = newAnnView;
}
else if(newAnnView.type == CustomType2)
{
ann2 = newAnnView;
}
else if(newAnnView.type == CustomType3)
{
ann3 = newAnnView;
}
}
}
}
Also, here's my viewForAnnotation method:
- (MKAnnotationView *)mapView:(MKMapView *)pMapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if([annotation class] == MKUserLocation.class)
{
return nil;
}
CustomAnnotationView *annotationView = [[CustomAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"WayPoint"];
annotationView.canShowCallout = YES;
annotationView.draggable = YES;
[annotationView setSelected:YES animated:YES];
[annotationView setRightCalloutAccessoryView:customCalloutButton];
return annotationView;
}
Now, eventually, I need to save the coordinates of these annotations, and here's where things go wrong. Sometimes, but only once in a while, ann1.annotation.coordinate.latitude and ann1.annotation.coordinate.longitude are both 0.0 (this happens with ann1, ann2, or ann3, just using ann1 for example purposes)! Why is this happening? I have a feeling it has something to do with an object reference issue since the MKAnnotationView is still intact, but the annotation is cleared out. Maybe it's bad that I'm assigning the reference with ann1 = newAnnView? Am I supposed to use viewForAnnotation?
Does anyone see something I'm doing wrong?
UPDATE
I looked over my MKAnnotation subclass and I noticed that while I am defining a coordinate property according to the docs, I was not #synthesizing it in my implementation file. I have now added that and I haven't been able to replicate the issue yet...if this ends up being the "fix", I'm still very confused why my code would have worked most of the time without the #synthesize. Maybe I didn't actually fix it, and I'm setting myself up for disappointment later.
I don't think you;re supposed to use didAddAnnotationViews like that. Usually the flow is as follows:
Create an MKAnnotation, or an instance of your subclass of it
Assign the strings you mentioned
Call [mapView addAnnotation:myAnnotation]
In viewForAnnotation create an MKAnnotationView (or CustomAnnotationView( based on the annotation provided as a parameter
When you need to save the coordinates you can either loop through the mapView.annotations array, or if you have kept thre3 variables called ann1, ann2, ann3 could save them one by one.
Of course if you've found a better way or this doesn't fit something else in your app you don't need to use it, but this is the only flow I have seen thus far.

New annotation create each time - dequeueReusableAnnotationViewWithIdentifier

This is my code:
- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation class] == MKUserLocation.class) {
return nil;
}
static NSString *identifier=#"an";
MKAnnotationView *pinView = nil;
pinView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (pinView == nil)
{
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
pinView.canShowCallout = YES;
NSLog(#"NEW ONE CREATED");
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
It seems that is not working and pinView is always nil because I can see the nslog for each pin created.
Two thoughts:
Don't assume that just because an annotation has scrolled off the current map view's current region, that it is automatically available for being dequeued and reused for another annotation immediately. It's quite possible that MKMapView is going to hang onto annotation views, not making them available for reuse immediately. For example, I could imagine some internal optimization that might hang on to annotation views that are near the map's current region in case that the user scrolls their map such that that previous annotation view is now visible again. MKMapView probably wants to avoid creating and recreating the same annotation view as the user scrolls the MKMapView back and forth.
In my experience, you have to scroll some distance before the old annotation views are made available for reuse.
This is extremely unlikely, but in addition to my observation above, we should note that your viewForAnnotation references some external variable, mapView, rather than using the aMapView that was passed as a parameter to the method. Most likely this is some instance variable that is pointing to the same MKMapView, and everything is fine, but if that mapView variable is nil, the dequeueReusableAnnotationViewWithIdentifier will always return nil, too. You might want to change your viewForAnnotation to use the aMapView parameter to remove this ambiguity.

viewForAnnotation didn't get called (iOS noob)

I know this question has been asked by others before and i have read them in this forum previously, but I have tried all the proposed approach without luck so far so I decided to post this question.
I have this piece of code below which is meant to change the pin icon on MKMapView. However the viewForAnnotation function doesn't even seem to get called by the MKMapView. People said that they got some problems with delegating the function to the file's owner which can be done by dragging the map view in .xib file to the file owner or defining myMap.delegate = self. I have done both ways but still get nothing.
Really appreciate for any help for my problem, thanks.
CODE:
- (MKPinAnnotationView*)myMap:(MKMapView*)myMap viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *pin = [[MKPinAnnotationView alloc]initWithAnnotation:annotation reuseIdentifier:#"CustomPin"];
UIImage *icon = [UIImage imageNamed:#"bustour.png"];
UIImageView *iconView = [[UIImageView alloc] initWithFrame:CGRectMake(8,0,32,37)];
if(icon == nil)
NSLog(#"image: ");
else
NSLog(#"image: %#", (NSString*)icon.description);
[iconView setImage:icon];
[pin addSubview:iconView];
pin.canShowCallout = YES;
pin.pinColor = MKPinAnnotationColorPurple;
return pin;
}
DELEGATION
Your delegate method is named incorrectly as myMap:viewForAnnotation:.
The viewForAnnotation delegate method must be named mapView:viewForAnnotation: like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView
viewForAnnotation:(id < MKAnnotation >)annotation
{
//code goes here...
}
The map view will look for that exact method name.
If not found, it won't call it and will create a default red pin instead.
You can change the name of the internal parameters but not the method name.
For example, this would also be ok:
- (MKAnnotationView *)mapView:(MKMapView *)myMap
viewForAnnotation:(id < MKAnnotation >)annotation
{
//code goes here...
}
import bellow class in your .h class..
#import <MapKit/MapKit.h>
and also add MKMapViewDelegate in your this class like bellow...
#interface PTAViewController : UIViewController<MKMapViewDelegate>{
///...... your code..
}
#end
As I understand, you setting outlet, not delegate. To set delegate do like that:

Properties of custom Annotation don't visible in delegate methods

I have many custom annotations to whom I gave the property (NameTable and ID). I set this property before AddAnnotation the moment of creation, but these property in the delegate methods are no longer visible. I have multiple annotation associated with elements of tables taken from a database. How can I make them visible in the delegate methods?
- (void)viewDidLoad
{
//......
for(int i=0; i<6; i++){ //loop for create multiple annotations
AnnotationCustom *annotationIcone =[[AnnotationCustom alloc]initWithCoordinates:coord
title:self.myTable.title subTitle:self.myTable.address];
annotationIcone.nameTable = [NSString stringWithFormat:#"%#", self.myTableName];
annotationIcone.ID = i+1;
[self.mapView addAnnotation: annotationIcone;
//.....
}
But in the delegate methods:
(MKAnnotationView *)mapView:(MKMapView *)mapview viewForAnnotation:(id
<MKAnnotation>)annotation
{
NSLog(#"The name of table is:#"%#", annotation.nameTable);
//property 'nameTable' not found on object of type '_strong id <MKAnnotation>
NSLog (#The name of table is:#%#", annotation.ID);
//property 'ID' not found on object of type '_strong id <MKAnnotation>
//......
}
In another method:
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view{
NSLog(#"The name of table is %#", self.myTableName);
// here I get the name of last table open and not the name of table selected
}
In viewForAnnotation, you need to tell the compiler that the annotation is actually an AnnotationCustom object.
So you first need to do this:
AnnotationCustom *annotationCustom = (AnnotationCustom *)annotation;
and then try to access the nameTable property..
In the didSelectAnnotationView method, if what you want is to get the nameTable value of the annotation selected you need to do the following:
AnnotationCustom *annotationCustomSelected = (AnnotationCustom *)view.annotation;
NSLog(#"table name of annotation selected: %#", annotationCustomSelected.nameTable);

Get URI from MKAnnotation selected on a MKmapview

This may be a design error instead of a syntax error, so let me know if I'm coding in the wrong direction here.
I'm new to Cocoa Touch/Objective-C and I've been working through tutorials on Core Data and MapKit. So far the app places an annotation on a mapview for items fetched from Core Data. I made a custom annotation object (called MapPin) that also holds a URI (NSURL*) pointing to the object in Core Data that it represents. When the user selects an annotation I want to use the URI property of that annotation to find which object in coredata the annotation represents.
This works if I add the annotation to the mapview beforehand. Here I add a MapPin annotation for each object "thing"
//viewWillAppear function in my ViewController
NSURL *uri = [[thing objectID] URIRepresentation];
MapPin *annotation = [[[MapPin alloc] initWithName:thing.common description:thing.latin coordinate:coordinate uri:uri] autorelease];
NSLog(#"MapPin URI: %#", [annotation.uri absoluteString]); //This works!
[_mapView addAnnotation:annotation];
NSLog(#"Placed Map Pin: %#", thing.common);
Later, after the user selects an annotation and clicks a button in the annotationView callout, I want to access the URI for the selected annotation
//the UIButton click action in my view controller
MSPTreesAppDelegate *del = (MSPTreesAppDelegate *)[UIApplication sharedApplication].delegate;
NSArray *annArray = _mapView.selectedAnnotations;
MapPin *selectedPin = [annArray objectAtIndex:0];
NSLog(#"Selected Pin Name: %#", selectedPin.name); //Works fine
NSLog(#"URI PASSED: %#", [selectedPin.uri absoluteString]); //Doesn't work
NSURL* uriForTree = selectedPin.uri; //also doesn't work
I notice in the debugger that before I add the annotation to the mapview, the URI property shows the correct string. After the (MapPin) annotation is selected from the map view, the URI property in the debugger just shows "invalid summary".
When I try to access the URI property the program ends and I get a "Thread 1: Program Received signal: "EXC_BAD_ACCESS"." error. The Log doesn't show anything helpful besides that.
I assume that my custom URI property for my MKAnnotation object isn't supported by the MKAnnotationView or the MKMapView, but I can't figure out where the URI is getting lost. Is there a way I can retrieve my URI property from the selected annotation? Other suggestions for methods to accomplish the same concept are welcome.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"MapPin";
if ([annotation isKindOfClass:[MapPin class]]) {
NSLog(#"Annotation is a MapPin");
TreeAnnotationView *annotationView = (TreeAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[TreeAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
//Place details button in callout
UIButton * detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[detailButton setTitle:annotation.title forState:UIControlStateNormal];
[detailButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
annotationView.rightCalloutAccessoryView = detailButton;
return annotationView;
}
return nil;
}
Why not make your MapPin be a subclass of NSManagedObject and have it implement MKAnnotation?
#interface MapPin : NSManagedObject <MKAnnotation>
#end
That way you one and only one model object to represent the pin and that model object is persisted through CoreData.
But your "EXC_BAD_ACCESS" and "invalid summary" indicate that the URI isn't' being retained (strong if you're using ARC).

Resources