MKAnnotationView for userLocation pin iPhone - objective-c

I have an application that uses MKMapView and at some point in the app I need to remove all the current pins in the map using
[mapView removeAnnotations:mapView.annotations]
And then I want to show again the current user location
mapView.showUserLocation = YES
But I can only make it reappear as a regular pin, because the userLocation view class its not public so I cant return views of that type. Here is my code
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id<MKAnnotation>)annotation
MKPinAnnotationView* annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:#"currentloc"];
if (!annView) {
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
annView.pinColor = MKPinAnnotationColorRed;
annView.animatesDrop=TRUE;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
if ([annotation isKindOfClass:[DraggableAnnotationAM class]] ) annView.draggable =YES;
return annView;
}
else {
if ([annotation isKindOfClass:[DraggableAnnotationAM class]] ) annView.draggable = YES;
annView.annotation = annotation;
return annView;
}
Also I have found through reflection that
MKUserLocationView
is the class that is used to display the current location, but because its not public its not safe to use and my app keeps crashing and Im sure theres a easier way.
Is it possible to do what I want, or should I just never remove the user location annotation of the mapView?
Thanks in advance

If you only want to show a blue dot for the user's location in stead of a regular pin, return nil in this mapView delegate function. Alternatively return your own MKAnnotationView (subclass) object:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil // or return a custom MKAnnotationView
} else { // etc.

Try doing it with an NSMutableArray. Hope this helps you solving your problem!
NSMutableArray *annotation = [[NSMutableArray alloc] init];
for (id <MKAnnotation> annot_ in [mapView annotations])
{
if ( [annot_ isKindOfClass:[MKUserLocation class]] ) {
}
else {
[annotation addObject:annot_];
}
}
[mapView removeAnnotations:annotation];
[annotation release];
annotation = nil;

Related

How can I Drop and Drag a pin on a map in Objective-C

Hope someone can help, I'm trying to implement dropping one single Pin on a mapView. But I want the pin to be able to be dragged around and and dropped and for this annotation to be separate from the annotation for the user's location. Whilst the below code works, it creates duplicate pins for a user's touch and the pins can not be dragged around:
- (void)dropPinFromPress:(UIGestureRecognizer *)recognizer {
// Add Pin from User's Touch
if (recognizer.state != UIGestureRecognizerStateBegan) {
return;
}
// convert touched position to map coordinate
CGPoint userTouch = [recognizer locationInView:self.mapView];
CLLocationCoordinate2D mapPoint = [self.mapView convertPoint:userTouch toCoordinateFromView:self.mapView];
// Add Pin from user's touch
Annotation *pin = [[Annotation alloc]initWithLocation:mapPoint];
[self.mapView addAnnotation:pin];
}
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
MKAnnotationView *a = [ [ MKAnnotationView alloc ] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
// a.title = #"test";
if ( a == nil )
a = [ [ MKAnnotationView alloc ] initWithAnnotation:annotation reuseIdentifier: #"currentloc" ];
NSLog(#"%f",a.annotation.coordinate.latitude);
NSLog(#"%f",a.annotation.coordinate.longitude);
CLLocation* currentLocationMap = [[[CLLocation alloc] initWithLatitude:a.annotation.coordinate.latitude longitude:a.annotation.coordinate.longitude] autorelease];
[self coordinatesToDMS:currentLocationMap];
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:a reuseIdentifier:#"currentloc"];
if(a.annotation.coordinate.longitude == mapView.userLocation.coordinate.longitude || a.annotation.coordinate.latitude == mapView.userLocation.coordinate.latitude )
{
if ([annotation isKindOfClass:MKUserLocation.class])
{
//user location view is being requested,
//return nil so it uses the default which is a blue dot...
return nil;
}
//a.image = [UIImage imageNamed:#"userlocation.png"];
//a.pinColor=[UIColor redColor];
}
else
{
// annView.image =[UIImage imageNamed:#"map-pin.png"];
//PinFirst=FALSE;
//annView.pinColor = [UIColor redColor];
// annView.annotation=annotation;
}
annView.animatesDrop = YES;
annView.draggable = YES;
annView.canShowCallout = YES;
return annView;
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
NSLog(#"map Drag");
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState
fromOldState:(MKAnnotationViewDragState)oldState;
{
NSLog(#"pin Drag");
if (newState == MKAnnotationViewDragStateEnding)
{
CLLocationCoordinate2D droppedAt = view.annotation.coordinate;
NSLog(#"Pin dropped at %f,%f", droppedAt.latitude, droppedAt.longitude);
CLLocation* draglocation = [[[CLLocation alloc] initWithLatitude:droppedAt.latitude longitude:droppedAt.longitude] autorelease];
}
}

iOS 5 custom annotation not showing its title

I wrote this code to create a custom annotation image
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
static NSString *google = #"googlePin";
if ([annotation isKindOfClass:[myClass class]])
{
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:google];
if (!annotationView)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:google];
annotationView.image = [UIImage imageNamed:#"pin.png"];
}
return annotationView;
}
return nil;
}
The image is appearing on the map; however when I click on it nothing happen, no title nor subtitle.
Do you guys have any idea?
When you override viewForAnnotation, you have to set canShowCallout to YES (the default on a new view you alloc/init is NO).
If you don't override that delegate method, the map view creates a default red pin with canShowCallout already set to YES.
However, even with canShowCallout set to YES, the callout will still not appear if the annotation's title is nil or blank (empty string).
(But again, if the title is not nil and not blank, the callout won't show unless canShowCallout is YES.)
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:google];
if (!annotationView)
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:google];
annotationView.image = [UIImage imageNamed:#"pin.png"];
annotationView.canShowCallout = YES; // <-- add this
}
else
{
// unrelated but should handle view re-use...
annotationView.annotation = annotation;
}
return annotationView;

Map view annotations with different pin colors

I have an array with over 200 objects and I am trying to perform a loop through each of them.
Each object will have a yes/no field and I want to display a different coloured marker dependent on that yes / no value.
From what I can see is happening my loop is going through each object first and then all the annotation is added at the end for each object .
Since I perform a check within my loop through the array on the yes no value when all the annotation is added to my map, it will use the yes/no value from the last object in the array when it goes to plot for all.
How can I have it so that the marker will be different dependent on the yes/no value for each individual element?
My code is
for (i = 0; i < [appDelegate.itemArray count]; i++) {
item_details *tempObj = [appDelegate.itemArray objectAtIndex:i];
location.latitude = [tempObj.lat floatValue];
location.longitude = [tempObj.lon floatValue];
current_yesno = tempObj.yesno;
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc]initWithTitle:tempObj.name andCoordinate:location];
[self.mapView addAnnotation:newAnnotation];
[newAnnotation release];
}
with my annotation code as follows
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation{
MKPinAnnotationView *annView=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentloc"];
if(current_yesno == YES){
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
annView.animatesDrop=NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
return annView;
}
and current_yesno is declared in my .h file.
The viewForAnnotation delegate method isn't necessarily called immediately after you do addAnnotation and it can also be called at other times by the map view when it needs to get the view for an annotation (while your code is doing something completely different).
So you can't depend on the value of an ivar being in sync with some code outside that delegate method.
Instead, add the yesno property to your custom MapViewAnnotation class, set it when creating the annotation and then access its value in viewForAnnotation through the annotation parameter (ie. the map view is giving you a reference to the exact annotation object it wants the view for).
Example:
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc] init...
newAnnotation.yesno = tempObj.yesno; // <-- set property in annotation
[self.mapView addAnnotation:newAnnotation];
Then in viewForAnnotation:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
if (![annotation isKindOfClass:[MapViewAnnotation class]])
{
// Return nil (default view) if annotation is
// anything but your custom class.
return nil;
}
static NSString *reuseId = #"currentloc";
MKPinAnnotationView *annView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
if (annView == nil)
{
annView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseId];
annView.animatesDrop = NO;
annView.canShowCallout = YES;
annView.calloutOffset = CGPointMake(-5, 5);
}
else
{
annView.annotation = annotation;
}
MapViewAnnotation *mvAnn = (MapViewAnnotation *)annotation;
if (mvAnn.yesno)
{
annView.pinColor = MKPinAnnotationColorGreen;
}
else
{
annView.pinColor = MKPinAnnotationColorRed;
}
return annView;
}
MKPinAnnotationView *pin = (MKPinAnnotationView *) [self.mapView dequeueReusableAnnotationViewWithIdentifier: #"id"];
if (pin == nil)
{
pin = [[MKPinAnnotationView alloc] initWithAnnotation: annotation reuseIdentifier: #"id"] ;
}
else
{
pin.annotation = annotation;
}
pin.pinTintColor=[UIColor blueColor];
pin.canShowCallout = true;

Add annotation pin on touch using MapKit and UIGestureRecognizer

I have some problems with adding annotations when the user touch the map.
I'm using UIGestureRecognizer to detect the user's touch.
When a long press is detected, I'm calling this function :
- (void)handleLongPressGesture:(UIGestureRecognizer*)gestureRecognizer{
if (gestureRecognizer.state == UIGestureRecognizerStateEnded) return;
NSLog(#"long press");
CGPoint touchPoint = [gestureRecognizer locationInView:mapView];
CLLocationCoordinate2D touchMapCoordinate =
[mapView convertPoint:touchPoint toCoordinateFromView:mapView];
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] init];
[rdvAnnotation initWithCoordinate:touchMapCoordinate];
[mapView removeAnnotations:mapView.annotations];
[mapView addAnnotation:rdvAnnotation];
[rdvAnnotation release]; }
I cans see the NSLog in the console and the rdvAnnotation is initialized with the good coordinate.
I don't understand why I can't see my annotation on the map.
Here's my - (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation method:
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
if ([annotation isKindOfClass:[RdvAnnotation class]])
{
static NSString* RdvAnnotationIdentifier = #"rdvAnnotationIdentifier";
MKPinAnnotationView* pinView = (MKPinAnnotationView *)
[mapView dequeueReusableAnnotationViewWithIdentifier:RdvAnnotationIdentifier];
if (!pinView)
{
MKPinAnnotationView* customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:RdvAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
return nil;}
My viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
[mapView setShowsUserLocation:TRUE];
[mapView setMapType:MKMapTypeStandard];
[mapView setDelegate:self];
CLLocationManager *locationManager=[[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager startUpdatingLocation];
self.navigationItem.title = #"Rendez-vous" ;
}
I just noticed something that seems weird :
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] init];
[rdvAnnotation initWithCoordinate:touchMapCoordinate];
You're calling init twice on the annotation object. You should just do it this way :
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] initWithCoordinate:touchMapCoordinate]];
EDIT :
If you have code in your init method that you don't want to lose, keep the init and change the value of the coordinate property :
RdvAnnotation *rdvAnnotation = [[RdvAnnotation alloc] init];
rdvAnnotation.coordinate = touchMapCoordinate;
In viewDidLoad you are creating a new map view object.
First, this new map view object is not being added as a subview to self.view so it exists in memory but is invisible.
Second, you probably already have a map view object placed in the view in Interface Builder so you don't need to create a new one.
So what is probably happening is that when you add the annotation, it is being added to the map view object in memory but not the one that is visible (the one that was created in IB).
Instead of creating a new map view in viewDidLoad, connect the mapView IBOutlet to the map view in Interface Builder.

Showing the Pin Information by Default

I am working with MKPinAnnotationView annotations pins over a MKMapView. Into the view that I am developing, there is a map centered on a pin. When I call the view the pin drops down and if I make a click over it, it shows the annotation information (canShowCallout = YES). How I can get this callout just when I open the view? Without making a click over the annotation pin.
Thanks for reading.
Edited:
I am using this code.
- (void)viewDidLoad {
[super viewDidLoad];
AddressAnnotation *annotation = [[[AddressAnnotation alloc] initWithCoordinate:self.currentAnnotationCoordinate] autorelease];
[self.mapView addAnnotation:annotation];
[self zoomCoordinate:self.currentAnnotationCoordinate];
}
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation {
// If it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
else { // Handles the other annotations.
// Try to dequeue an existing pin view first.
static NSString *AnnotationIdentifier = #"AnnotationIdentifier";
MKPinAnnotationView *pinView = (MKPinAnnotationView *)[self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationIdentifier];
if (!pinView) {
// If an existing pin view was not available, creates one.
MKPinAnnotationView *customPinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:AnnotationIdentifier] autorelease];
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
// Adds a detail disclosure button.
UIButton *rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self action:#selector(showDetails:) forControlEvents:UIControlEventTouchUpInside];
customPinView.rightCalloutAccessoryView = rightButton;
return customPinView;
} else
pinView.annotation = annotation;
}
return nil;
}
- (void)mapViewDidFinishLoadingMap:(MKMapView *)theMapView {
AddressAnnotation *annotation = [[[AddressAnnotation alloc] initWithCoordinate:self.currentAnnotationCoordinate] autorelease];
for (id<MKAnnotation> currentAnnotation in mapView.annotations) {
if ([currentAnnotation isEqual:annotation]) {
[mapView selectAnnotation:currentAnnotation animated:FALSE];
}
}
}
Edited:
Calling didAddAnnotationViews: instead of mapViewDidFinishLoadingMap: (as aBitObvious has commented), it works fine.
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
id<MKAnnotation> myAnnotation = [self.mapView.annotations objectAtIndex:0];
[self.mapView selectAnnotation:myAnnotation animated:YES];
}
Possible duplicate: How to trigger MKAnnotationView's callout view without touching the pin?
You want to call [mapView selectAnnotation:].