New post added for Marker and Annotation that can be used in SwiftUI.
How to Use Annotation and Marker on a SwiftUI Map – May 2024 (iOS 18).

Updated for Swift and UIKit (built with iOS 18).
The MKPointAnnotation is the basic version of an MKAnnotation that can be used to display a pin along with a title and subtitle. These can come in handy for displaying basic information on a map. If you want to do more than show a point on a map with a title, then you will need to use MKAnnotation and MKAnnotationView. We’ll look at both of those later in this tutorial. Note that you can change the pin as well and provide a custom image. I’ll show you how in the tutorial below.
The screenshot to the left shows what an MKPointAnnotation looks like in its basic form. This is what this short tutorial will cover today. To get started, create a new application and select Swift and Storyboard as the options.
Creating the Class
Lets begin with our ViewController class by adding a MapView:
class ViewController: UIViewController {
var mapView: MKMapView!
All of the remaining work will be done in ViewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Set up the map view
mapView = MKMapView(frame: view.bounds)
view.addSubview(mapView)
// Set the location
let coordinate = CLLocationCoordinate2D(latitude: 51.49795, longitude: -0.174056)
let span = MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002)
let region = MKCoordinateRegion(center: coordinate, span: span)
mapView.setRegion(region, animated: false)
// Create the annotation
let pizzaAnnotation = MKPointAnnotation()
pizzaAnnotation.title = "Matthew's Pizza"
pizzaAnnotation.subtitle = "Best Pizza in Town"
pizzaAnnotation.coordinate = coordinate
mapView.addAnnotation(pizzaAnnotation)
}
This part is done in three sections. The first sets up the mapView and adds it to the view, see lines 5 and 6.
Next we set the location. In this example I’m just specifying some coordinates that happen to be in London, England. We do this by creating a CLLocationCoordinate2D and setting the latitude and longitude.
We then need to set the span. This is how close the map will zoomed in. 0.002 on both axis is down at street level. If you increase the numbers then the map will zoom out.
Next is the region which is creating an MKCoordinateRegion that needs the centre (the coordinates set up just prior to the span), and also accepts the span.
We then call setRegion on the mapView and pass in the region as well as set animated to false.
Finally, we create the annotation. The first step is to create the MKPointAnnotation object and then we set its title, subtitle, and pass in the coordinate.
We then add this annotation to the mapView.
If you run the app now, you will see the map zoomed into London on Exhibition Road, and will see a small annotation.
Using the MKMapViewDelegate
If you tapped on the annotation in the previous example, you will see the annotation get larger, but nothing else happens. To make it react to touch we can make the class conform to the MKMapViewDelegate:
class ViewController: UIViewController, MKMapViewDelegate {
Add in the MKMapViewDelegate to the class as seen above.
mapView = MKMapView(frame: view.bounds)
mapView.delegate = self
Set the delegate to self in ViewDidLoad.
After ViewDidLoad, but still in the ViewController class, add the following method:
// Detect taps on annotations
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
guard let annotationTitle = view.annotation?.title ?? nil else { return }
let alert = UIAlertController(
title: "Pizza Time!",
message: "Visit \(annotationTitle) for the best slices in town!",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
alert.addAction(UIAlertAction(title: "Order Now", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
This method is a delegate method that is called when an annotation is selected. In here we can define some logic to show an alert.
In here we first get the title of the annotation. The annotation is passed in the delegate as “view”, so in our example of just one annotation we know that it will be the Matthew’s Pizza one added in the example earlier. We take this title on line 3.
Next we set up a UIAlertController and provide it with a title, a message (which uses the annotationTitle from a few lines above), and sets the preffered style to an alert.
We then add in our actions which are “Cancel” and “Order Now”. I haven’t set a handler here because it would take the tutorial off topic.
We then present this alert.
If you test now you should be able to tap the annotation and see an alert on the view.
Enhancing the Annotation
We can enhance the annotation a little by adding left and right callout accessories. Replace the current delegate method with the following:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Skip user location
if annotation is MKUserLocation {
return nil
}
let identifier = "PizzaAnnotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
// Left callout accessory
let leftIcon = UIImageView(image: UIImage(systemName: "flame.fill"))
leftIcon.tintColor = .orange
leftIcon.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
annotationView?.leftCalloutAccessoryView = leftIcon
// Right callout accessory
let infoButton = UIButton(type: .detailDisclosure)
annotationView?.rightCalloutAccessoryView = infoButton
} else {
annotationView?.annotation = annotation
}
return annotationView
}
In this example we use viewFor annotation delegate method. This is called each time it puts an annotation on the view and can be used to modify the view for the annotation.
We first return nil if this is the users current location. We don’t need to modify the view for that in our case.
We create a new String called identifier and set it to something, in this case “PizzaAnnotation”
We create our annotationView. We use the mapView.dequeueReusableAnnotationView which is part of a recycling mechanism to see if there are any annotation views that can be reused because they are no longer on the visible part of the view. If one exists that can be reused, it uses that. If not, it creates a new one. In short, this helps with performance.
We then set the annotationView to be able to show callouts.
Next, we create the left callout and set an image. When the image is created and the tint colour and other settings are correct, we add this image to the leftCalloutAccessoryView of the annotation view.
For the right side, I opted to have a button, so created a UIButton and added that to the rightCalloutAccessoryView.
The updated annotationView is then returned and this is what is put on the map.
Earlier you will have seen reference to
Adding the calloutAccessoryControlTapped Delegate Method
Because we added a button to the rightCalloutAccessoryView we need to respond to that being tapped. We do that with the following delegate method which also includes the implentation I added:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let alert = UIAlertController(title: "Pizza Time!", message: "Visit Matthew's Pizza for the best slices in town!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alert.addAction(UIAlertAction(title: "Order Now", style: .default))
present(alert, animated: true)
}
This delegate, like the one nearer the beginning of this tutorial, sets up a UIAlertController and presents this.
If we had implement buttons on both the left and right and you needed to know which was pressed you can compare the control to the view as follows:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.leftCalloutAccessoryView {
print("Left accessory tapped")
} else if control == view.rightCalloutAccessoryView {
print("Right accessory tapped")
}
.
.
.
This checks if the control is equal to leftCalloutAccessoryView or right…. This maybe useful if you had information on the left and a navigation button on the right.
If you have any questions, please comment below.
Alex says
How would you edit this to make it so each annotation object has a different leftCalloutAccessoryView?
Matthew says
If depends on what you are wanting to change. If just an image within a box then you just specify imageNamed: as something else. If you want to change the actual accessory view to look different for various types of annotation then you would subclass MKPointAnnotation… (a subclass for each type of annotation you want) and then in the MKAnnotationView method (viewForAnnotation) you would have this line:
if ([annotation isKindOfClass:[MKPointAnnotation class]])
And add more else if statements:
else if ([annotation isKindOfClass:[MyCustomAnnotation1 class]])
…
…
…
else if ([annotation isKindOfClass:[MyCustomAnnotation2 class]])
You would then set the call out different for each annotation.
How you go about it depends on what you want to achieve though. Let me know how it goes, or ask away if you still need help.
E. Newton says
Thank you. Thank you so very much. I have spent 8 hours trying to drop a pin at a specific location and every example I looked at was complicated with arrays or some ridiculous nested stuff. Maybe I’m not very bright, but your example I understand. Great work for us beginners.
akshay says
good explanation….
Shaun says
How to add customized button on top of the annotation?
Matthew says
You could investigate putting a custom UIView in to this property: rightCalloutAccessoryView. This accepts a UIView which would allow you to instantiate a button in there.
Eugene says
Thanks mate, exellent tutorial!
Trond says
Thanks a lot!
paul says
Hi there! thanks a lot for your tutorial :)
Here is my question: instead of having a UIAlertView, I’d like to have another view controller. I know apple released a sample “map callouts”, but I still cannot understand how to do it (they’re doing it with .xib files while I prefer working with storyboards…). I’ve been searching the internet all day but I’m quite lost now (I could not find any tutorials or complete explanation about it!) Could you help me with it?
Thanks,
Best
Craig says
It would be great if you updated this tutorial for Swift?
zühal calayoÄlu says
Hi ! I get an error in MKPointAnnotation Tutorial .It’s thread 1 signal sigabrt error.Can you help me?
Matthew says
Can you provide the code you are using?
Juan says
Thank you very much matthew. Very well explained info for annotation views
jayant rawat says
on click of annotation i want to create a view with image ,buttons ,text at the bottom of map view…how can i do it? i don’t want to show title,subtitle or any callout buttons..i want to show all description of annotation location in this custom view on the bottom of map view…
Shaklee Ahmed says
A very Simple and Great Tutorial. Very Easy to understand. Awesome Bro.
Naushad Qamar says
Thanks
Adnan says
can you change this tutorial to swift please ??
Matthew says
Working on the Swift version now. Should be ready in the next day or so. I’ll post a link.
Andy says
Hi Matthew
Any progress on the Swift version of this tutorial?
Leigh says
What would one add in order to programmatically move our annotation from point A to point B at the touch of a button?
What would one add in order to programmatically move our annotation through an array of points using a timer?
What would one add in order to tap and drag our annotation around the map?
Leigh says
A Swift version should be a separate post. My questions are for Objective-C.
Bajrang Sinha says
but after scrolling the map custom view say pizza icon converted into red pin. Help me out to solve this problem. Thank You.
Rafiullah says
how we add custom annotation for multiple annotation.plz explain it sir.
Hemant says
This tutorial really helped me to learn map. thanks