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.
Related
I want to create custom MKAnnotationView (with image) without a callout, but expandable after touch. Here is what i have in mind:
1) First the MKAnnotation looks like this:
2) Then after tap on it this should expand and looks like this:
I've started with customing MKAnnotationView and have a class:
#interface CustomPinAnnotation : NSObject <MKAnnotation>
The class have method:
-(MKAnnotationView*)annotationView {
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:self reuseIdentifier:#"AnnotationIdentifier"];
annotationView.enabled = YES;
annotationView.image = [[UIImage imageNamed:#"star"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
return annotationView;
}
And what i try to do is set a customView as a subview to the MKAnnotationView, but when i don't set The image it will not responding (this is pretty logical after i've think about it), but got the feeling that I'm doing something very nasty here, and should be a way to override MKAnnotationView somehow.
-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
enemyAnnotationView.image = [UIImage imageNamed:#"firstImage.png"];
}
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
enemyAnnotationView.image = [UIImage imageNamed:#"SecondImage.png"];
}
Try like this first method is for the View for annotation that time your first image will be appears,
Second method will run when user select annotation
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);
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.
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:
I am trying to learn MAP for iPhone.
What I have right now is below.
Created new project
Added framework for MAP
Brought map object on storyboard (UIViewController)
Run the project.
What I see is, its not showing any location. When I change location in xcode, it shows me the dot at location.
What I wanted is, by default it should show me the PIN to the location that I will set by using latitude and longitude. Also the map should be zoomed. What I meant by zoom is, I should see the location with lets say 13 zoom effect. Right now, I see world map on screen.
Any idea how to get this done?
You can center your map around a location by doing something like this:
MKCoordinateRegion mapRegion;
mapRegion.center.latitude = aLatitude;
mapRegion.center.longitude = aLongitude;
mapRegion.span.latitudeDelta = 0.005;
mapRegion.span.longitudeDelta = 0.005;
self.mapView.region = mapRegion;
Use the span values to determine the zoom level you want.
In order to show a pin you need to create an annotation with the coordinates of your location and then add it to the map.
Also, check out this tutorial.. http://www.raywenderlich.com/2847/introduction-to-mapkit-on-ios-tutorial
Dot is showing your current location.
If you want to add a pin with coordinate you should call addAnnotation method with object which conforms to MKAnnotation protocol. Such object has a property coordinate (you should add it to your class):
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
Also you should add MKMapViewDelegate protocol to your controller and implement -mapView:viewForAnnotation: method. It works as -tableView:viewForRowAtIndexPath:.
- (MKAnnotationView *)mapView:(MKMapView *)_mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]]) {
return nil;
}
static NSString *annotationIdentifier = #"annotation";
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:annotationIdentifier]; // Reusing
if (!annotationView) {
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annotationIdentifier];
pinView.animatesDrop = YES;
annotationView = pinView;
}
else {
annotationView.annotation = annotation; // Reusing already created pin as UITableViewCell does
}
return annotationView;
}
Then when you call
MKMapView *mapView = ...;
id<MKAnnotation> obj = ...;
[mapView addAnnotation:obj];
The pin would be placed on map.
For zoom look there. There is a handy category for those purposes.
If you want to remove current location dot you should find an object with class MKUserLocation in mapView.annotations and then call [mapView removeAnnotation:userLocationDot].
For creating an application with Map you need to implement the MKAnnotation, MKMapViewDelegate delgates.
This is a good tutorial for you.