I have a MapView with two pins on it (custom pins).
Both pins are set to draggable, but my problem is before I can drag one of them I first have to select it before I can start dragging it. This means two taps on the screen.
I know about this answer, but he only has one pin ons his map and it seems to me only one pin at a time can be selected so setting the [MyPin setSelected:YES]; wouldn't help me in this case.
Thanks for the help!
//Custom pin on mapview
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKAnnotationView *MyPin=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
MyPin.draggable = YES;
//Get annotaion title to determine what image to use
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc]init];
annotationPoint = annotation;
if([annotationPoint.title isEqualToString:#"user"])
{
MyPin.image = [UIImage imageNamed:#"userLocation_pin"];
MyPin.centerOffset = CGPointMake(-13, -5); //Offset custom image to display at the exact pin point GPointMake([left/right], [up/down]);
}
else if ([annotationPoint.title isEqualToString:#"destination"])
{
MyPin.image = [UIImage imageNamed:#"destination_pin_up"];
MyPin.centerOffset = CGPointMake(-13, -5); //Offset custom image to display at the exact pin point GPointMake([left/right], [up/down]);
}
return MyPin;
}
Managed to solve my own problem by adding the [MyPin setSelected:YES]; inside the if statements like this:
//Custom pin on mapview
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKAnnotationView *MyPin=[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
MyPin.draggable = YES;
//Get annotaion title to determine what image to use
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc]init];
annotationPoint = annotation;
if([annotationPoint.title isEqualToString:#"user"])
{
MyPin.image = [UIImage imageNamed:#"userLocation_pin"];
MyPin.centerOffset = CGPointMake(-13, -5); //Offset custom image to display at the exact pin point GPointMake([left/right], [up/down]);
[MyPin setSelected:YES];
}
else if ([annotationPoint.title isEqualToString:#"destination"])
{
MyPin.image = [UIImage imageNamed:#"destination_pin_up"];
MyPin.centerOffset = CGPointMake(-13, -5); //Offset custom image to display at the exact pin point GPointMake([left/right], [up/down]);
[MyPin setSelected:YES];
}
return MyPin;
}
Related
I have an MKMapView that I add pins to. They load correctly with their relevant graphics but if I zoom in and zoom back out they loose their graphic and turn into a standard red pin with the only customisation being the pin name (even my disclosure indicator disappear).
So far to try and fix it I've tried:
Tried png’s, checked on faster device, Changed everything from MKPinAnnotation to MKAnnotation, returning to a normal MKAnnotation instead of my custom CBAnnotation, Various sample codes for loading custom pins, Lowered quality of map overlay in case it was a loading issue but still an issue.
- (void)addPins {
mapPinsArray = [[NSMutableArray alloc] init];
for (MapPoint *mappoint in mapPointsArray) {
CBAnnotation *annotation = [[CBAnnotation alloc] init];
annotation.coordinate = CLLocationCoordinate2DMake(mappoint.loclat, mappoint.loclong);
annotation.title = mappoint.stopAreaName;
annotation.mapPoint = mappoint;
[mapPinsArray addObject:annotation];
[self.myMapView addAnnotation:annotation];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(CBAnnotation *)annotation {
if ([annotation isKindOfClass:[MKUserLocation class]]) {
//do nothing
return nil;
} else {
MKAnnotationView *annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"trailPoint"];
annotationView.canShowCallout = YES;
ButtonWithData *accessoryViewButton = [[ButtonWithData alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[accessoryViewButton setBackgroundImage:[UIImage imageNamed:#"right_arrow"] forState:UIControlStateNormal];
accessoryViewButton.buttonData = annotation.mapPoint;
[accessoryViewButton addTarget:self action:#selector(disclosureButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
annotationView.rightCalloutAccessoryView = accessoryViewButton;
if (![[NSUserDefaults standardUserDefaults] boolForKey:annotation.mapPoint.stopAnimalName]) {
annotationView.image = [annotation.mapPoint lockedPinImage];
} else {
annotationView.image = [annotation.mapPoint unlockedPinImage];
}
return annotationView;
}
}
Fixed my own problem. I subclassed MKMapView to GenericMapView so that my code was cleaner (removing showsUserLocation, zoom/scroll/rotateEnabled, showsCompass etc. from the actual View Controller) but this meant the delegate wasn't setting correctly and viewForAnnotation wasn't being called on the pins reload.
I'm currently building an "Uber-like" application, not in the same business but same design, and I would like to know what should I do if I want to represent their "Set pickup location" View/Button.
I already saw this post : Customize MKAnnotation Callout View? which was about Custom Annotation Callout View
In older version it looked like a Custom Callout View, but now it's a bit different, and I don't know where to start if I want to have the same result :
Thanks for your help !
To set different image on left accessory and right accessory, use this code.
// set different pins colors
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *annotationView = nil;
if( [annotation isKindOfClass:[YOURANNOTATION class] ] )
{
static NSString * AnnotationID = #"YOURANNOTATION";
annotationView = [self.mapView dequeueReusableAnnotationViewWithIdentifier:AnnotationID];
if( annotationView == nil )
{
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:AnnotationID] ;
}
UIImage * flagImage = nil;
flagImage = [UIImage imageNamed:#"marker-map#1x.png"];
[annotationView setImage:flagImage];
annotationView.canShowCallout = YES;
// add an image to the callout window
UIImageView *leftIconView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"marker-map#1x.png"]];
annotationView.leftCalloutAccessoryView = leftIconView;
//adding right button accessory
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = infoButton;
//image size and adding image on left accessory
CGRect resizeRect;
resizeRect.size = flagImage.size;
resizeRect.origin = (CGPoint){0.0f, 0.0f};
UIGraphicsBeginImageContext(resizeRect.size);
[flagImage drawInRect:resizeRect];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
annotationView.image = resizedImage;
}
return annotationView;
}
Then to call selected annotation
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
YOURANNOTATION *annotation=(YOURANNOTATION*)view.annotation;
//do something with your annotation
}
Am trying to add custom annotations on MKMapView and implement custom callOut view on tap of annotation.
Am following this link for the same. The issue is when I add the custom annotation didSelectAnnotationView is called on its own and the popOver callout is shown, even when the user has not clicked the annotation
Here is my code:
-(void)callAddAnnotationsLocal{
[_mapView removeAnnotations:[_mapView annotations]];
CLLocationCoordinate2D coord = {.latitude = 43.3998170679, .longitude = -95.1288472486};
[_mapView addAnnotations:[self annotationsLocal]];
}
-(NSArray *)annotationsLocal{
self.annotArrayLocal = nil;
self.annotArrayLocal = [[NSMutableArray alloc] init];
for (int i=0; i<[arrAmmenities count];i++)
{
MKAnnotationView *propertyAnnotation = [[MKAnnotationView alloc] init];
UIFont *font = [UIFont fontWithName:#"Arial" size:18];
NSDictionary *userAttributes = #{NSFontAttributeName: font,
NSForegroundColorAttributeName: [UIColor blackColor]};
NSString *latStr = [NSString stringWithFormat:#"%#",[[arrAmmenities objectAtIndex:i]objectForKey:#"latitude"]];
float latitude = [latStr floatValue];
NSString *longStr = [NSString stringWithFormat:#"%#",[[arrAmmenities objectAtIndex:i]objectForKey:#"longitude"]];
float longitude = [longStr floatValue];
CLLocationCoordinate2D coord = {.latitude = latitude, .longitude = longitude};
PinAnnotation * annotation = [[PinAnnotation alloc] init];
[annotation setCoordinate:coord];
[annotation setType:[[arrAmmenities objectAtIndex:i] objectForKey:#"category"]];
[annotation setTag:i];
[self.mapView addAnnotation:annotation];
[self.annotArrayLocal addObject:propertyAnnotation];
}
return _annotArrayLocal;
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation {
MKAnnotationView *annotationView;
NSString *identifier;
if ([annotation isKindOfClass:[PinAnnotation class]]) {
// Pin annotation.
identifier = #"Pin";
annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
if([[(PinAnnotation *)annotation type] isEqualToString:#"Gas Stations"])
annotationView.image = [UIImage imageNamed:#"marker_gas"];
else if([[(PinAnnotation *)annotation type] isEqualToString:#"Golf Courses"])
annotationView.image = [UIImage imageNamed:#"marker_golf"];
else if([[(PinAnnotation *)annotation type] isEqualToString:#"Restaurants"])
annotationView.image = [UIImage imageNamed:#"marker_restaurant"];
else
annotationView.image = [UIImage imageNamed:#"marker_hospital"];
annotationView.canShowCallout = NO;
}
return annotationView;
}
-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
// [mapView deselectAnnotation:view.annotation animated:YES];
MapAnnotViewController *controller = [[UIStoryboard storyboardWithName:#"MapStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"sidMapAnnotVC"];
// controller.annotation = view.annotation; // it's useful to have property in your view controller for whatever data it needs to present the annotation's details
wyPopController = [[WYPopoverController alloc] initWithContentViewController:controller];
wyPopController.delegate = self;
[wyPopController setPopoverContentSize:CGSizeMake(200.0, 100.0)];
[wyPopController presentPopoverFromRect:view.frame
inView:view.superview
permittedArrowDirections:WYPopoverArrowDirectionAny
animated:YES];
}
Where am I getting wrong? How do I solve this?
A couple of thoughts:
In annotationsLocal, you are not only instantiating PinAnnotation objects and adding them to a map, but you are also instantiating MKAnnotationView objects, building an array of them, and then returning that array, which is then added as annotations to the map view.
That second array of MKAnnotationView should not be built at all, and certainly shouldn't be added as annotations of the map view. Don't conflate annotation objects (which represent the coordinates of points on a map) and annotation view objects (which represent visual representation of those annotations as they are rendered on the map).
As an aside, and unrelated to your problem, you are instantiating unnecessary annotation views in viewForAnnotation, too. You should dequeue an annotation view, and only instantiate a new one if you didn't successfully retrieve one that was to be reused:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation {
MKAnnotationView *annotationView;
if ([annotation isKindOfClass:[PinAnnotation class]]) {
// Pin annotation.
NSString *identifier = #"Pin";
annotationView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView) {
annotationView.annotation = annotation
} else {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
annotationView.canShowCallout = NO;
}
if ([[(PinAnnotation *)annotation type] isEqualToString:#"Gas Stations"])
annotationView.image = [UIImage imageNamed:#"marker_gas"];
else if([[(PinAnnotation *)annotation type] isEqualToString:#"Golf Courses"])
annotationView.image = [UIImage imageNamed:#"marker_golf"];
else if([[(PinAnnotation *)annotation type] isEqualToString:#"Restaurants"])
annotationView.image = [UIImage imageNamed:#"marker_restaurant"];
else
annotationView.image = [UIImage imageNamed:#"marker_hospital"];
}
return annotationView;
}
But I'm not seeing anything here that would cause didSelectAnnotationView to be called, unless that incorrect process of adding annotation views as annotations had some weird side effect that caused this. If you're still seeing didSelectAnnotationView called, I might suggest adding a breakpoint there and looking at the call stack and seeing if you can see where that was being invoked. But hopefully the fixing of annotationsLocal will fix it.
I am trying to use a custom image on my MKAnnotationView when I use the following code I get no image on my annotation. I have checked in debug to ensure the image is being properly loaded into the UIImage.
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"String"];
if(!annotationView) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"String"];
UIButton *directionButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *directionIcon = [UIImage imageNamed:#"IconDirections"];
[directionButton setImage:directionIcon forState:UIControlStateNormal];
annotationView.rightCalloutAccessoryView = directionButton;
}
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
There are two main issues:
The frame of the custom callout button is not set making it essentially invisible.
An MKAnnotationView is being created but its image property (the image of the annotation itself -- not the callout button's) is not set. This makes the whole annotation invisible.
For issue 1, set the button's frame to some appropriate value. For example:
UIImage *directionIcon = [UIImage imageNamed:#"IconDirections"];
directionButton.frame =
CGRectMake(0, 0, directionIcon.size.width, directionIcon.size.height);
For issue 2, set the annotation view's image (or create an MKPinAnnotationView instead):
annotationView.image = [UIImage imageNamed:#"SomeIcon"];
Additionally, you should handle view re-use correctly by updating the annotation property.
Complete example:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
MKAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:#"String"];
if(!annotationView) {
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"String"];
annotationView.image = [UIImage imageNamed:#"SomeIcon"];
UIButton *directionButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *directionIcon = [UIImage imageNamed:#"IconDirections"];
directionButton.frame =
CGRectMake(0, 0, directionIcon.size.width, directionIcon.size.height);
[directionButton setImage:directionIcon forState:UIControlStateNormal];
annotationView.rightCalloutAccessoryView = directionButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
}
else {
//update annotation to current if re-using a view
annotationView.annotation = annotation;
}
return annotationView;
}
In order for the callout to be shown, the annotation must be selected. To do this programmatically, call:
[mapView selectAnnotation:annotation animated:YES];
where annotation is the specific MKAnnotation for which you want a callout displayed.
You'll almost certainly want to put this in - (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views.
There are a few caveats to consider, so here are two other posts that have some great answers and relevant discussions:
How to trigger MKAnnotationView's callout view without touching the pin?
Wanted: How to reliably, consistently select an MKMapView annotation
Below, I have created an Annotation for my map which works perfectly and shows up with the title and subtitle as it should.
But I wish to add a small image to the left of the annotation but can't figure out what I need to do to the below code to make it work.
// Annotation
CLLocationCoordinate2D poseLocation;
poseLocation.latitude = POSE_LATITUDE;
poseLocation.longitude = POSE_LONGITUDE;
Annotation *myAnnotation = [[Annotation alloc] init];
myAnnotation.coordinate = poseLocation;
myAnnotation.title = #"Pose Beauty Salon";
myAnnotation.subtitle = #"100, Moneyhaw Road";
[self.myMapView addAnnotation:myAnnotation];
You will need to set the delegate of your myMapView first and implement the viewForAnnotation delegate method.
There you will return MKAnnotationView instance which has a property leftCalloutAccessoryView:
The view to display on the left side of the standard callout bubble.
The default value of this property is nil. The left callout view is
typically used to display information about the annotation or to link
to custom information provided by your application. The height of your
view should be 32 pixels or less.
In the leftCalloutAccessoryView, you can assign your image there. For example:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *)[self.myMapView dequeueReusableAnnotationViewWithIdentifier:#"annotation"];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"annotation"];
UIImageView *imageView = //initialize your image view here
annotationView.leftCalloutAccessoryView = imageView;
}
annotationView.annotation = annotation;
return annotationView;
}
PS: Apparently you have asked similar question before here: Add image to the left of my annotations. I am not sure you need to post another question. Please try to implement this first.
-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation{
MKPinAnnotationView *MyPin=[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"current"];
myImage = [UIImage imageNamed:imagename];
CGRect cropRect = CGRectMake(0.0, 0.0,35.0, 35.0);
myImageView = [[UIImageView alloc] initWithFrame:cropRect];
myImageView.clipsToBounds = YES;
myImageView.image = myImage;
MyPin.leftCalloutAccessoryView =myImageView;
MyPin.highlighted=YES;
MyPin.canShowCallout=YES;
return MyPin;
}
It works for me , try this one