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 view
mapView.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:
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
Uicollectionview Performance - _Updatevisiblecellsnow
How Do iOS Push Notifications Work
Converting a Vision Vntextobservation to a String
How to Pretty Print Swift Dictionaries to the Console
Xcode/Simulator: How to Run Older iOS Version
How to Animate Add Uisearchbar on Top of Uinavigationbar
Uitableviewcell Separator Disappearing in iOS7
Differencebetween Embedded Binaries and Linked Frameworks
Launch Screen Storyboard Not Displaying Image
How to Manage Sessions with Afnetworking
Cocoapods Setup Stuck on Pod Setup Command on Terminal
Disable the Interactive Dismissal of Presented View Controller
Where Should I Be Setting Autolayout Constraints When Creating Views Programmatically
Change Table to Edit Mode and Delete Rows Inside a Normal Viewcontroller
MySQL and Swift - Upload Image and File || Would It Be Better to Use Alamofire