Custom Pin Image in Annotationview in iOS

Custom pin image in annotationView in iOS

The accepted answer doesn't work, as it has annotationView uninitialized in else block.

Here's a better solution. It dequeues annotation view if possible or creates a new one if not:

// https://stackoverflow.com/a/38159048/1321917
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
guard !(annotation is MKUserLocation) else {
return nil
}

// Better to make this class property
let annotationIdentifier = "AnnotationIdentifier"

var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}

if let annotationView = annotationView {
// Configure your annotation view here
annotationView.canShowCallout = true
annotationView.image = UIImage(named: "yourImage")
}

return annotationView
}

Swift - MKPointAnnotation custom pin image

The way I usually do it is to create a new swift file that will be your custom annotation which inherits from MKAnnoatation. Example below

import MapKit

class MyAnnotation: NSObject, MKAnnotation {
let title: String?
let subtitle: String?
let coordinate: CLLocationCoordinate2D
var image: UIImage? = nil

init(title: String, subtitle: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
//self.image
super.init()
}
}

You will need to initialise your annotation where the CLCoordinate is compulsory. Then set the image property with your custom image. MyAnnotation.image = "myImage.png". You will then need to add your annotation to your map viewmapView.addAnnotations(MyAnnotation).
I also implement the method below from the MKMapViewDelegate (Make sure your inherit this in your class). This is so that the user can tap on the annotation and receive information about it. Hope this helps.

In your view controller:

let marker = MyAnnotation(title: "title" as! String, subtitle: "subtitle" as! String, coordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude))
marker.image = UIImage("my image.png")
self.mapView.addAnnotations(marker)

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? MyAnnotation {
let identifier = "identifier"
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.image = annotation.image //add this
annotationView?.canShowCallout = true
annotationView?.calloutOffset = CGPoint(x: -5, y: 5)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
return annotationView
}
return nil
}

How to custom the image of MKAnnotation pin

If you want that rounded border, you can render it yourself, or easier, subclass MKMarkerAnnotationView rather than MKAnnotationView:

class CustomAnnotationView: MKMarkerAnnotationView {
override var annotation: MKAnnotation? {
didSet { configure(for: annotation) }
}

override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

glyphImage = ...
markerTintColor = ...

configure(for: annotation)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func configure(for annotation: MKAnnotation?) {
displayPriority = .required

// if doing clustering, also add
// clusteringIdentifier = ...
}
}

That way, not only do you get the circular border, but you get all of the marker annotation view behaviors (shows the title of the annotation view below the marker, if you select on the marker annotation view, it becomes larger, etc.). There’s a lot of marker annotation view behaviors that you probably don’t want to have to write from scratch if you don’t have to. By subclassing MKMarkerAnnotationView instead of the vanilla MKAnnotationView, you get all those behaviors for free.

For example, you could:

class CustomAnnotationView: MKMarkerAnnotationView {
static let glyphImage: UIImage = {
let rect = CGRect(origin: .zero, size: CGSize(width: 40, height: 40))
return UIGraphicsImageRenderer(bounds: rect).image { _ in
let radius: CGFloat = 11
let offset: CGFloat = 7
let insetY: CGFloat = 5
let center = CGPoint(x: rect.midX, y: rect.maxY - radius - insetY)
let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: .pi, clockwise: true)
path.addQuadCurve(to: CGPoint(x: rect.midX, y: rect.minY + insetY), controlPoint: CGPoint(x: rect.midX - radius, y: center.y - offset))
path.addQuadCurve(to: CGPoint(x: rect.midX + radius, y: center.y), controlPoint: CGPoint(x: rect.midX + radius, y: center.y - offset))
path.close()
UIColor.white.setFill()
path.fill()
}
}()

override var annotation: MKAnnotation? {
didSet { configure(for: annotation) }
}

override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

glyphImage = Self.glyphImage
markerTintColor = #colorLiteral(red: 0.005868499167, green: 0.5166643262, blue: 0.9889912009, alpha: 1)

configure(for: annotation)
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func configure(for annotation: MKAnnotation?) {
displayPriority = .required

// if doing clustering, also add
// clusteringIdentifier = ...
}
}

That yields:

Sample Image

Obviously, when you set glyphImage, set it to whatever image you want. The old SF Symbols doesn't have that “drop” image (though iOS 14 has drop.fill). But supply whatever 40 × 40 pt image view you want. I'm rendering it myself, but you can use whatever appropriately sized image from your asset catalog (or from the system symbols) that you want.


As an aside, since iOS 11, you wouldn't generally wouldn't implement mapView(_:viewFor:) at all, unless absolutely necessary (which it isn't in this case). For example, you can get rid of your viewFor method and just register your custom annotation view in viewDidLoad:

override func viewDidLoad() {
super.viewDidLoad()

mapView.register(CustomAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)

...
}

How to set custom pin image in Mapview swift ios?

  mapview.delegate = self

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard !(annotation is MKUserLocation) else {
return nil
}

let annotationIdentifier = "Identifier"
var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}

if let annotationView = annotationView {

annotationView.canShowCallout = true
annotationView.image = UIImage(named: "yourImagename”)
}
return annotationView
}

Cant set Custom Pin Image In MapView, Swift 3

Your issue is that you´re missing add your UIViewController as delegate of your MKMapView so you need to add this line in your viewDidLoad as I said in my comments

self.ProfileMapView.delegate = self

I also recommend you to use the extension pattern to make your code more readable

extension YourViewController : MKMapViewDelegate{

func mapView(_ ProfileMapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

if annotation.isMember(of: MKUserLocation.self) {
return nil
}

let reuseId = "ProfilePinView"

var pinView = ProfileMapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if pinView == nil {
pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)}
pinView!.canShowCallout = true
pinView!.image = UIImage(named: "CustomPinImage")

return pinView

}
}

How to add title and custom image to a specific location pin in MKMapView?

Sample code you need to change latitude, longitude, title and subtitle.

ViewContrller.m

#import "ViewController.h"
#import <MapKit/MapKit.h>
#import "V3AnnotationView.h"

@interface ViewController () {
IBOutlet MKMapView *myMapView;
}
@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSMutableArray *arrAnnotation = [[NSMutableArray alloc] init];

V3AnnotationView *annotation = [[V3AnnotationView alloc] init];
annotation.title = @"Ahmedabad";
annotation.image = [UIImage imageNamed:@"icon1.png"];
annotation.subtitle = @"City";
annotation.coordinate = CLLocationCoordinate2DMake(23.0225, 72.5714);
[arrAnnotation addObject:annotation];

V3AnnotationView *annotationAirport = [[V3AnnotationView alloc] init];
annotationAirport.title = @"Airport";
annotationAirport.image = [UIImage imageNamed:@"airport.png"];
annotationAirport.subtitle = @"Airport";
annotationAirport.coordinate = CLLocationCoordinate2DMake(23.0734, 72.6266);
[arrAnnotation addObject:annotationAirport];

V3AnnotationView *annotationCarParking = [[V3AnnotationView alloc] init];
annotationCarParking.title = @"Car";
annotationCarParking.image = [UIImage imageNamed:@"carparking.png"];
annotationCarParking.subtitle = @"Free parking";
annotationCarParking.coordinate = CLLocationCoordinate2DMake(23.0225, 72.6714);
[arrAnnotation addObject:annotationCarParking];

V3AnnotationView *annotationWifi = [[V3AnnotationView alloc] init];
annotationWifi.title = @"Wifi";
annotationWifi.image = [UIImage imageNamed:@"wifi-512.png"];
annotationWifi.subtitle = @"Free wifi";
annotationWifi.coordinate = CLLocationCoordinate2DMake(23.0225, 72.7714);
[arrAnnotation addObject:annotationWifi];

[myMapView addAnnotations:arrAnnotation];
}

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{

// Handle any custom annotations.
if ([annotation isKindOfClass:[V3AnnotationView class]])
{
V3AnnotationView *myAnnotation = (V3AnnotationView *)annotation;
// Try to dequeue an existing pin view first.
MKAnnotationView *pinView = (MKAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:@"CustomPinAnnotationView"];
if (!pinView)
{
// If an existing pin view was not available, create one.
pinView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"CustomPinAnnotationView"];
//pinView.animatesDrop = YES;
pinView.canShowCallout = YES;
pinView.calloutOffset = CGPointMake(0, 4);

} else {
pinView.annotation = annotation;
}
pinView.image = myAnnotation.image;
return pinView;
}
return nil;
}

You need to create class MKAnnotationView and copy below code into your class file

V3AnnotationView.h

#import <MapKit/MapKit.h>

@interface V3AnnotationView : MKAnnotationView
@property (nonatomic,assign) CLLocationCoordinate2D coordinate;
@property (nonatomic,copy) NSString *title;
@property (nonatomic,copy) NSString *subtitle;
@property (nonatomic,retain) UIImage *image;
@end

V3AnnotationView.m

#import "V3AnnotationView.h"

@implementation V3AnnotationView
@synthesize title,subtitle,coordinate,image;

Size image pin annotation

There is not a maximum size of the pin image. You need to resize UIImage.

    let annotationIdentifier = "SomeCustomIdentifier"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView?.canShowCallout = true

// Resize image
let pinImage = UIImage(named: "pin maps.png")
let size = CGSize(width: 50, height: 50)
UIGraphicsBeginImageContext(size)
pinImage!.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()

annotationView?.image = resizedImage

let rightButton: AnyObject! = UIButton(type: UIButtonType.detailDisclosure)
annotationView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
annotationView?.annotation = annotation
}

How to set DIFFERENT image pins in different locations SWIFT

You can add an image property to your AnnotationPin class and then in viewForAnnotation you use a conditional downcast to see if you are dealing with one of your annotations. If you are then you can use the image property

class AnnotationPin:  NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
let subtitle: String?
let image: UIImage?

init(title:String, subtitle: String, image: UIImage, coordinate: CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
self.image = image
super.init()
}}

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

guard let myAnnotation = annotation as? AnnotationPin else {
return nil
}

let annotationIdentifier = "AnnotationIdentifier"

var annotationView: MKAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
}
else {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}

if let annotationView = annotationView {

annotationView.canShowCallout = true
annotationView.image = myAnnotation.image
}

return annotationView
}


Related Topics



Leave a reply



Submit