I am implementing a custom calloutView on my mapView and I am attempting to implement action buttons from the custom calloutView. I am having trouble finding ways to detect the buttons tapped with respect to the calloutViews.
Below are my implementation:
//At MapViewController
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if !annotation.isKind(of: Post.self) {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView?.canShowCallout = true
annotationView?.animatesDrop = true
} else {
annotationView?.annotation = annotation
}
let postAnnotation = annotation as! Post
let calloutView = DetailCalloutView()
calloutView.titleLabel.text = ...
calloutView.timestampLabel.text = ...
calloutView.postContentTextView.text = ...
calloutView.profileImageView.image = ...
calloutView.deleteButton.addTarget(self, action: #selector(deleteButtonTap), for: .touchUpInside)
calloutView.chatButton.addTarget(self, action: #selector(chatButtonTap), for: .touchUpInside)
annotationView?.detailCalloutAccessoryView = calloutView
return annotationView
}
#objc func deleteButtonTap(_ sender: MKAnnotationView) {
let post = sender.annotation as? Post
let index = postArray.index(of: post) //Is there a way to implement something like this here??
...
}
#objc func chatButtonTap() {
print("123")
}
//At Post
class Post: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var post: String?
//All other properties
init(coordinate: CLLocationCoordinate2D, post: String, ...) {
self.coordinate = coordinate
self.post = post
...
}
}
//At DetailCallOutView
class DetailCalloutView: UIView {
let containerView: UIView = {
...
return v
}()
//All other UI elements
let chatButton: UIButton = {
let b = UIButton()
...
return b
}()
let deleteButton: UIButton = {
let b = UIButton()
...
return b
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() {
addSubview(containerView)
//adding subviews for all UI elements
containerView.addSubview(cButton)
containerView.addSubview(dButton)
//setting up constraints for all UI elements
}
}
I am able to detect the chat and delete buttons, but am unable to obtain the post with respect to the postArray or annotation. Is there a way to do so?
First action is of type that target added to , which is UIButton here
calloutView.deleteButton.tag = <#setToIndexOfArr#>
calloutView.deleteButton.addTarget(self, action: #selector(deleteButtonTap), for: .touchUpInside)
//
#objc func deleteButtonTap(_ sender: UIButton) {
let item = modelArr[sender.tag]
}
Related
In my application, I have stored coordinates in FireBase and am displaying those coordinates on a Map View in the code below. The current location of the user is also being harnessed. I would like the locations I have stored in FireBase to be represented by the generic Apple Maps pins, but I do not know how to get the app to work unless I use custom images as pins instead. Also, the generic "blue dot" of the user displayed on the map to represent the current location will not appear for me. In different projects in which the locations are stored in-app rather than in FireBase, the generic pins and blue dot appear just as I would like them to. How can I fix this?
I have tried changing bits and pieces of the code, but I am rather new to Swift and the programming world in general, and cannot find a solution.
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBAction func mapSwitch(_ sender: UISwitch) {
if (sender.isOn == true) {
mapView.mapType = MKMapType.standard
}
else {
mapView.mapType = MKMapType.hybrid
}
}
var tappedAnnotation : MKAnnotation?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let view = mapView.dequeueReusableAnnotationView(withIdentifier: "Annotation") {
view.annotation = annotation
view.image = UIImage(named: "pin")
return view
} else {
let view = MKAnnotationView(annotation: annotation, reuseIdentifier: "Annotation")
view.image = UIImage(named: "pin")
view.isEnabled = true
view.canShowCallout = true
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
return view
}
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
tappedAnnotation = view.annotation
performSegue(withIdentifier: "showAnnotationDetails", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showAnnotationDetails", let dest = segue.destination as? AnnotationDetails {
dest.annotation = tappedAnnotation
}
}
func createAnnotations ( _ annotations : [String:[String:Any]] ) {
mapView.removeAnnotations(mapView.annotations)
for (_,values) in annotations {
if let latDouble = values["latitude"] as? Double, let longDouble = values["longitude"] as? Double {
let lat = CLLocationDegrees( latDouble )
let long = CLLocationDegrees( longDouble )
let coord = CLLocationCoordinate2D(latitude: lat, longitude: long)
let annotation = MKPointAnnotation()
annotation.coordinate = coord
annotation.title = values["name"] as? String ?? ""
annotation.subtitle = values["info"] as? String ?? ""
mapView.addAnnotation(annotation)
}
}
}
let manager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
mapView.delegate = self
mapView.mapType = MKMapType.standard
let ref = Database.database().reference()
ref.child("annotations").observe(.value) { snapshot in
print(snapshot)
if let annotations = snapshot.value as? [String:[String:Any]] {
self.createAnnotations(annotations)
} else {
print("Data received not formatted as expected")
}
}
}
Use MKPinAnnotationView instead of MKAnnotationView
Set mapView.showsUserLocation = true to show user location (blue dot)
I want to have a custom MKAnnotationView. I've created a xib file in IB and set its class to MyAnnotationView.
class MyAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var busIcon: UIImageView!
}
Here's how the xib looks like - it has a textLabel and a busIcon linked:
I'm using the viewFor annotation delegate method to create views for all annotations:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MyAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationIdentifier") as? MyAnnotationView {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.image = UIImage(named: "Delivery")
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
}
return annotationView
}
}
The annotationView.image = UIImage(named: "Delivery")
&
AnnotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
are there just to check if the code is working and display a sample view on the map, as they use the standard properties inherited from MKAnnotationView.
I don't know how to make the viewFor annotation method use the XIB I have created. Could anyone please help me with that? I searched for the solution, but only found something relevant in Obj C.
Thank you!
1- Create a view subclass of UIView with xib say CallView
2- Inside viewforAnnotation
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "id")
let customView = Bundle.main.loadNibNamed("CallView", owner: self, options: nil).first! as! CallView
// here configure label and imageView
annotationView.addSubview(customView)
UPDATED CODE BASED ON Sh-Khan's answer
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MyAnnotationView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MyAnnotationView
var annotationView: MyAnnotationView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
let customView = annotationView.subviews.first as! MyAnnotationView
customView.frame = annotationView.frame
customView.textLabel.text = (annotationView.annotation?.title)!
}
return annotationView
}
}
**Create Custom MKPointAnnotation Class**
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var id : Int
var url : String
init(id : Int , url : String ) {
self.id = id
self.url = url
}
}
Create Xib for MarkerView Class for Annotation View
class MarkerView: MKAnnotationView {
#IBOutlet weak var imgVwUser: UIImageView!
init(annotation: CustomPointAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if (hitView != nil)
{
self.superview?.bringSubviewToFront(self)
}
return hitView
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let rect = self.bounds
var isInside: Bool = rect.contains(point)
if(!isInside)
{
for view in self.subviews
{
isInside = view.frame.contains(point)
if isInside
{
break
}
}
}
return isInside
}
}
Add MKMapView Delegates In Your ViewController
extension YourViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MarkerView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MarkerView
var annotationView: MarkerView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MarkerView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MarkerView(annotation: annotation as? CustomPointAnnotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = false // callout bubble
if let annotation = annotation as? CustomPointAnnotation {
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 66, height: 75)
let customView = annotationView.subviews.first as? MarkerView
customView?.frame = annotationView.frame
let image = annotation.url
let imageUrl = URL(string: image)
customView?.imgVwUser.sd_setImage(with: imageUrl, placeholderImage: UIImage(named:"defaults"), options: [.refreshCached], completed: nil)
}
}
return annotationView
}
}
}
ADD Annotation to the mapview
extension YourViewController {
func addAnnotation(){
let annotationsToRemove = mapView.annotations.filter { $0 !== mapView.userLocation }
mapView.removeAnnotations( annotationsToRemove )
var annotations: [CustomPointAnnotation] = []
for i in 0..<self.arrayData.count {
let customPoints = CustomPointAnnotation.init(id: arrayData[i].id ?? 0, url: arrayData[i].url)
let location = CLLocationCoordinate2DMake(self.arrayData[i].lat ?? 0, self.arrayData[i].lng ?? 0)
customPoints.coordinate = location
annotations.append(customPoints)
}
mapView.addAnnotations(annotations)
}
}
I'm trying to add a custom image for my pin annotations as well as change the colour of the custom images for some of the annotations. The colour is changed. However, the image doesn't show up. The default pins show instead.
Here's my code:
class MyPointAnnotation : MKPointAnnotation {
var pinTintColor: UIColor?
}
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate{
#IBOutlet weak var map: MKMapView!
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
if annotation is MKUserLocation {
return nil
}
if let annotation = annotation as? MyPointAnnotation {
annotationView?.pinTintColor = annotation.pinTintColor
annotationView?.image = UIImage(named: "BLog.png")
}
return annotationView
}
override func viewDidLoad() {
super.viewDidLoad()
self.map.delegate = self
let annotation1 = MyPointAnnotation()
annotation1.coordinate = CLLocationCoordinate2DMake([LatArray], [LonArray])
annotation1.title = NameArray
annotation1.pinTintColor = .red
let annotation2 = MyPointAnnotation()
annotation2.coordinate = CLLocationCoordinate2DMake([LatArray2], [LonArray2])
annotation2.title = NameArray2
annotation2.pinTintColor = .green
}
}
The image "BLog.png" is located in the main bundle.
I've assigned the MKMapView as a delegate.
But the image still won't change.
You need to use MKAnnotationView instead of MKPinAnnotationView to add a custom image for your pin annotations.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
if // Image pin // {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "image")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "image")
annotationView?.canShowCallout = true
annotationView?.image = UIImage(named: "BLog.png")
let rightButton: AnyObject! = UIButton(type: UIButtonType.detailDisclosure)
annotationView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
annotationView?.annotation = annotation
}
return annotationView
} else {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
if let annotation = annotation as? MyPointAnnotation {
annotationView?.pinTintColor = annotation.pinTintColor
}
return annotationView
}
}
I have an MKMapView on which I would like to add annotations on a long press gesture. After adding the annotation, I would like to select the annotation view. Quite a simple request I assumed.
The problem is that I am using the new MKMarkerAnnotationView with a custom rightCalloutAccessoryView and a custom detailCalloutAccessoryView. It is not very well documented yet, but the WWDC 2017 Session 237 states that the callout will be displayed when there is more than a title/subtitle.
Unfortunately, this is not the case for me. When I programmatically (and manually) select the annotation, I get a weird double selected state where I can see the callout AND the marker:
Here is the annotation view code:
import Foundation
import MapKit
#available(iOS 11.0, *)
protocol TemporaryUserAnnotationViewDelegate: class {
func temporaryUserAnnotationViewDidTapOk(title: String?, userAnnotationView: TemporaryUserAnnotationView)
}
#available(iOS 11.0, *)
class TemporaryUserAnnotationView: MKMarkerAnnotationView {
weak var delegate: TemporaryUserAnnotationViewDelegate?
var textField: UITextField!
init(annotation: TemporaryUserAnnotation) {
super.init(annotation: annotation, reuseIdentifier: TemporaryUserAnnotationMarkerAnnotationView.reuseIdentifier)
markerTintColor = .lbc_blue
canShowCallout = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(annotation: TemporaryUserAnnotation) {
let detailView = UIView()
detailView.translatesAutoresizingMaskIntoConstraints = false
textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholder = "Titre"
detailView.addSubview(textField)
detailView.heightAnchor.constraint(equalToConstant: 21).isActive = true
detailView.widthAnchor.constraint(equalToConstant: 100).isActive = true
textField.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
textField.centerYAnchor.constraint(equalTo: detailView.centerYAnchor).isActive = true
textField.heightAnchor.constraint(equalToConstant: 21).isActive = true
textField.widthAnchor.constraint(equalToConstant: 100).isActive = true
detailCalloutAccessoryView = detailView
let rightView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 50, height: 70)))
rightView.backgroundColor = .lbc_blue
let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: 50, height: 40)))
button.setTitle("OK", for: .normal)
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action: #selector(tapRightCalloutAccessoryViewButton), for: .touchUpInside)
rightView.addSubview(button)
rightCalloutAccessoryView = rightView
}
#objc func tapRightCalloutAccessoryViewButton() {
delegate?.temporaryUserAnnotationViewDidTapOk(title: textField.text, userAnnotationView: self)
}
}
and here is the controller code:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
print(#function)
if annotation is MKUserLocation { return nil }
if #available(iOS 11.0, *) {
switch annotation {
case let temporarayUserAnnotation as TemporaryUserAnnotation:
if let view = mapView.dequeue() as? TemporaryUserAnnotationView {
view.annotation = annotation
return view
} else {
let view = TemporaryUserAnnotationView(annotation: temporarayUserAnnotation)
view.delegate = self
view.configure(annotation: temporarayUserAnnotation)
return view
}
default:
return nil
}
} else {
let identifier = "marker"
if let view = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView {
view.annotation = annotation
return view
} else {
return MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
}
}
}
I simply add annotations this way:
func addUserAnnotation(coordinate: CLLocationCoordinate2D) {
let annotation = TemporaryUserAnnotation(coordinate: coordinate)
mapView.addAnnotation(annotation)
mapView.selectAnnotation(annotation, animated: true)
}
I also tried to implement the mapView(_:didAdd:) method but the result is even worse with the marker view in front of the callout.
Any help would be very appreciated! Thanks!
I found the following workaround: when you add your annotation, wait a second before programmatically selecting it:
func addUserAnnotation(coordinate: CLLocationCoordinate2D) {
let annotation = TemporaryUserAnnotation(coordinate: coordinate)
mapView.addAnnotation(annotation)
DispatchQueue.main.asyncAfter(wallDeadline: .now() + .seconds(1)) {
self.mapView.selectAnnotation(annotation, animated: true)
}
}
I still think this should not be necessary, so if you have a better solution, feel free to post it here!
I just encountered a similar problem because I had a UITapGestureRecognizer of my own competing with MKMapView's selection gesture recognizer. I created and selected the annotation on the tap event, but because both gesture recognizers were firing, and theirs was firing last, it was deselecting immediately.
My solution was to implement UIGestureRecognizerDelegate and become the delegate of my gesture recognizer, and implement the following:
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
I am creating a map application which allows emojis to be attached to locations. Currently, the image is displayed above the stock apple map annotation.
I wish to hide the stock pin image while still displaying the selected emoji by the user. Is this possible?
I have this code so far to perform this:
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
//let identifier = "MyPin"
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
let dictName = arrLocation[((annotation as? MyAnnotation)?.index)!]
let imgName = dictName.valueForKey("pin_img") as? String
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(imgName!)
if annotationView == nil
{
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: imgName)
annotationView!.canShowCallout = false
let name = dictName.valueForKey("name") as! String
let annot = MyAnnotation(titleName:name)
annotationView?.annotation = annot
}
else
{
annotationView!.annotation = annotation
}
let detailButton: UIButton = UIButton(type: UIButtonType.Custom)
// size and placement of emoji on map
detailButton.frame = CGRectMake(-34,-25,85,85)
detailButton.tag = ((annotation as? MyAnnotation)?.index)!
detailButton.addTarget(self, action:"emojiTap:", forControlEvents: UIControlEvents.TouchUpInside)
detailButton.setBackgroundImage(UIImage(named:(dictName.valueForKey("pin_img") as? String)!), forState: UIControlState.Normal)
annotationView!.addSubview(detailButton)
return annotationView
}
Custom annotation view
Instead of using an MKPinAnnotationView, use its superclass MKAnnotationView.
Responding to input
To respond to the user tapping on an annotation implement the mapView:didSelectAnnotation: delegate method. This delegate method is called when the annotation is tapped.
Note that you do not need to use the button inside the annotation view. This means the functionality in emojiTap: must be implemented by the mapView:didSelectAnnotationMethod:.
Example
Combining these
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
let dictName = arrLocation[((annotation as? MyAnnotation)?.index)!]
let imgName = dictName.valueForKey("pin_img") as? String
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(imgName!)
if annotationView == nil
{
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: imgName)
let name = dictName.valueForKey("name") as! String
let annot = MyAnnotation(titleName:name)
annotationView?.annotation = annot
}
else
{
annotationView!.annotation = annotation
}
annotationView!.canShowCallout = false
return annotationView
}
func mapView(_ mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
guard let annotation = view.annotation as? MyAnnotation else {
return
}
print("tapped annotation: \(annotation.index)
// Implement the code from emojiTap: here.
}