I am implementing a MKMapView based application. In that I am using a custom image instead of displaying a pin by using MKAnnotationView.Image property. It is not working. It keeps showing the standard pin in red but not the image I am pointing too. Any ideas?
- (void)setPin:(MKPinAnnotationView *)aPin forAnnotation:(id <MKAnnotation>)anAnnotation {
if (anAnnotation == self.userAnnotation) {
aPin.image = [UIImage imageNamed:#"youarehere.png"];
aPin.opaque = NO;
aPin.pinColor = MKPinAnnotationColorRed;
}
}
If you use MKPinAnnotationView like you are then you don't get to set the image only the pinColor. MKPinAnnotationView only has pinColor and animatesDrop as the properties.
You want to use the parent class MKAnnotationView. It will let you set the image.
Read the Class Reference for each and it will make sense. This is one of the times that Objective-C makes me crazy because when you are reading those long class names in the docs, it's easy to scan right over the "Pin" variation in the class name.
Related
Currently, when I try to add an annotation to the map in the user's location the annotation is not showing up. When I set the annotation (using the same code) besides the user's location it does show up.
The code:
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate // set coordinate
annotation.title = "Title"
annotation.subtitle = "Subtitle"
mapView.addAnnotation(annotation)
same issue here, it was working fine with iOS 12, now all annotations near the current location marker are not visible anymore. Critical information might be near the user location and needs to be displayed. This is mostly bad when you zoom out the map, as you don't know anymore if there is data since all annotations comes near the user location...
Is there a document that indicates this change from apple? I didn't find any... thanks in advance for someone that finds it.
If there is no options to turn off this behavior, the only workaround I can think of is to manually add the current location annotation ourselves...
In the callback -mapView:viewForAnnotation on your MKMapViewDelegate be sure that the MKAnnotationView you return leaves its displayPriority as its default value which is MKFeatureDisplayPriorityRequired otherwise MapKit clusters your annotation view with the user location.
I have a fix for those having issues. I was having issues in my production app as well because I was just adding the annotations to the map and never calling the viewForAnnoation method. I created a sample project and uploaded it to Github so you can review the code and see what I did. In short, you need to create a class that subclasses NSObject and conforms to the MKAnnotation protocol. If using MKLocalsearch like my project, pass the MKMapItem object back and create a new instance of the new class. You will also need to create a class and subclass MKMarkerAnnotationView.
class CustomMKMarkerSubclass: MKMarkerAnnotationView {
override var annotation: MKAnnotation? {
willSet {
if let _ = newValue as? MapLocation {
displayPriority = MKFeatureDisplayPriority.required
canShowCallout = true
}
}
}
}
For more detail, check the project below.
MapKit Demo Project
Using MKPinAnnotationView instead of MKAnnotationView has fixed my issue
- (MKAnnotationView *)mapView:(MKMapView *)tempMapView viewForAnnotation:(id <MKAnnotation>)tempAnnotation{
if ([tempAnnotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
NSString *identifer = #"Identifer";
MKPinAnnotationView *dPV = [[MKPinAnnotationView alloc] initWithAnnotation:tempAnnotation reuseIdentifier:identifer];
[dPV setSelected:YES];
[dPV setTag:102];
dPV.canShowCallout = YES;
[dPV setSelected:YES];
dPV.draggable = YES;
return dPV;}
It seems like iOS 14 fixed this issue
I have an array of objects that conform to <MKAnnotation>.
I load this array into my annotations using addAnnotations:.
In the method:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:
(id<MKAnnotation>)annotation
I have every pin load a custom image using:
annotationView.image = [UIImage imageNamed:#"purp_pin.png"];
However, I don't want all the pins to load with this image. I want it to load a different custom image/identifier depending on the properties the object that conformed to <MKAnnotation> had.
How would I do this?
If you have some custom property in your object that conforms to MKAnnotation, one way you can access it in viewForAnnotation to set the image is like this:
MyAnnotationClass *myAnnot = (MyAnnotationClass *)annotation;
if (myAnnot.someProperty == 42)
annotationView.image = [UIImage imageNamed:#"purp_pin.png"];
else
annotationView.image = [UIImage imageNamed:#"default.png"];
Make sure the image property is set regardless of whether annotation view is being dequeued or created.
I am writing an application that displays a thumbnail from a server in the leftCalloutAccessoryView. Before I actually get the image, I display a placemark image. Once the photo is downloaded, a method is called and I want to update that image without removing the original annotation and placing another. I just wan the image to switch from the placeholder to the downloaded thumbnail. Below is the code that does not work, but if someone could let me know if I'm on the right track, that would be great.
MyAnnotationClass *annotation=[[MyAnnotationClass alloc] initWithCoordinate:item.location];
[annotation setItem:item];
if(item.title){
annotation.name=item.title;
}
else{
annotation.name=#"no title";
}
[annotation setDescription:[[NSString alloc] initWithFormat:#"Views:%d Likes:%d Comments:%d",item.views,item.likes,item.comments]];
[annotation setImage:[[photo.thumb copyWithDimensions: CGSizeMake(32.0, 32.0)] autorelease]];
MKAnnotationView *av=[__mapView viewForAnnotation:annotation];
UIImageView *imageView=[[UIImageView alloc] initWithImage:annotation.image];
imageView.frame=CGRectMake(0.0, 0.0, 32.0, 32.0);
av.leftCalloutAccessoryView=imageView;
[annotation release];
I have been looking around here for quite sometime, but nothing that I can find will do what I need. Thanks
If the annotation is already on the map and you want to update it, don't create a new one.
Instead, find the annotation you want to update in the map view's annotations array and update its properties. Then call viewForAnnotation: on that existing annotation and update the leftCalloutAccessoryView.
Also, make sure the viewForAnnotation delegate method has logic that sets the leftCalloutAccessoryView to either the placeholder image or the actual image by checking the annotation's properties (and not always the placeholder image).
This other question has some sample code that may help.
I want to create a custom MKAnnotationView callout as shown in this image. I have tested several solutions but they only allow customization of the left/right images and title/subtitle. Can anybody please give me some source code or tutorial link for it?
Currently I am clueless. Please help.
I understand you want a pin with a custom callout.
We can't create a custom callout, but we can create an annotation with a completely customized view. So the trick is to add a second annotation when the first is selected, and make the 2nd annotation view look like a callout bubble.
This is the solution posted by users djibouti33 and jacob-jennings in the answer: MKAnnotationView - Lock custom annotation view to pin on location updates, which in turn is based in a blog post from Asynchrony Solutions. For explanation purposes, here is some UML from a forked project:
This is a big hack, but also the cleanest way I've seen to implement custom annotations.
Start with a NSObject "Content" class which has a coordinate, the class of the callout view to use (in the UML is AnnotationView, but you can create more and set them here), and a dictionary of random values with the title, photo url, etc. Use this class to initialize a MKAnnotation "Annotation" object.
#import <MapKit/MapKit.h>
#interface Content : NSObject
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
// ...
#interface Annotation : NSObject <MKAnnotation, AnnotationProtocol>
-(id) initWithContent:(Content*)content;
// ...
The Annotation implements AnnotationProtocol to announce it wants to handle the creation of its own MKAnnotationView. That is, your MKMapViewDelegate should have code like this:
- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
// if this is a custom annotation, delegate the implementation of the view
if ([annotation conformsToProtocol:#protocol(AnnotationProtocol)]) {
return [((NSObject<AnnotationProtocol>*)annotation) annotationViewInMap:mapView];
} else {
// else, return a standard annotation view
// ...
}
}
The view returned will be of type AnnotationView, which implements AnnotationViewProtocol to announce that it wants to handle selection/deselection. Therefore, in your map view controller, the methods mapView:didSelectAnnotationView: and mapView:didDeselectAnnotationView: should delegate in a similar way to what we saw before.
When the annotation is selected, a second annotation (CalloutAnnotation) is added, which follows the same behaviour, but this time the view returned (CalloutView) is initialized from a XIB, and contains Core Graphics code (in BaseCalloutView) to animate and replicate a callout.
The initializer of the CalloutView class:
- (id)initWithAnnotation:(CalloutAnnotation*)annotation
{
NSString *identifier = NSStringFromClass([self class]);
self = [super initWithAnnotation:annotation reuseIdentifier:identifier];
if (self!=nil){
[[NSBundle mainBundle] loadNibNamed:identifier owner:self options:nil];
// prevent the tap and double tap from reaching views underneath
UITapGestureRecognizer *tapGestureRecognizer = ...
}
return self;
}
To be able to push another view controller from the callout view I used notifications.
The SO answer I linked at the top contains two complete projects implementing this code (class names may differ). I have another project using the UML above at https://github.com/j4n0/callout.
I added custom UIButton in MKAnnotationView. And on click of that button I have shown popOver with rootViewController with the view similar as you have shown above.
I know this question is from 2011 but for people who still find it in a search:
In iOS 9 you have MKAnnotationView.detailCalloutAccessoryView which entirely replaces the standard callout.
Wish to specify the the color of a map pin other than the default red. From documentation, the MKPinAnnotationView class has a pinColor property where we can set the color. My question is : what is the proper way to associate this pinColor property with an annotation object?
Wish that somebody knowledgable on this could help ...
i think only three colors are defined for pinColor property.
enum {
MKPinAnnotationColorRed = 0,
MKPinAnnotationColorGreen,
MKPinAnnotationColorPurple
};
typedef NSUInteger MKPinAnnotationColor;
Here's the method to define it.
MKPinAnnotationView* customPinView = [[[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:MyAnnotationIdentifier] autorelease];
customPinView.pinColor = MKPinAnnotationColorPurple;
I hope I understand you question correctly.
I guess that would be part of your logic and that there's no one "correct" answer.
In one app I might have a plist containing definitions of all the objects in my app and their properties. In another more simple app I might just specify the color based upon an index when it's created for example.
If you just want to know how to set a property then follow Shrey's advice above