Mkmapview Mkpointannotation Tap Event

MKMapView MKPointAnnotation tap event

There are two ways of detecting user interaction with your annotation view. The common technique is to define a callout (that standard little popover bubble that you see when you tap on a pin in a typical maps app) for your MKAnnotationView. And you create the annotation view for your annotation in the standard viewForAnnotation method:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;

MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

return annotationView;
}

By doing this, you get a callout, but you're adding an right accessory, which is, in my example above, a disclosure indicator. That way, they tap on your annotation view (in my example above, a pin on the map), they see the callout, and when they tap on that callout's right accessory (the little disclosure indicator in this example), your calloutAccessoryControlTapped is called (in my example below, performing a segue to some detail view controller):

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
[self performSegueWithIdentifier:@"DetailsIphone" sender:view];
}

That's a very typical user experience on the small iPhone screen.

But, if you don't like that UX and you don't want the standard callout, but rather you want something else to happen, you can define your MKAnnotationView so that a callout is not shown, but instead you intercept it and do something else (for example, on iPad maps apps, you might show some more sophisticated popover rather than the standard callout). For example, you could have your MKAnnotationView not show a callout:

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;

MKAnnotationView *annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"loc"];
annotationView.canShowCallout = NO;

return annotationView;
}

But you can then manually handle didSelectAnnotationView to detect when a user tapped on your MKAnnotationView, in this example showing a popover:

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
[mapView deselectAnnotation:view.annotation animated:YES];

DetailsViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"DetailsPopover"];
controller.annotation = view.annotation;
self.popover = [[UIPopoverController alloc] initWithContentViewController:controller];
self.popover.delegate = self;
[self.popover presentPopoverFromRect:view.frame
inView:view.superview
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}

I include some screen snapshots for the user interface yielded by the above code in my answer here.

MKPointAnnotations touch event in swift

Several steps are necessary, here are some code snippets to get you started.

First you need a custom class for your pin annotation which holds the data you want to work with.

import MapKit
import Foundation
import UIKit

class PinAnnotation : NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0, longitude: 0)
private var _title: String = String("")
private var _subtitle: String = String("")

var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}

func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}

var title: String? {
get {
return _title
}
set (value) {
self._title = value!
}
}

var subtitle: String? {
get {
return _subtitle
}
set (value) {
self._subtitle = value!
}
}
}

Then you need a custom class for your MKMapView which conforms to the MKMapViewDelegate protocol. Implement the method viewForAnnotation there:

import MapKit
import CLLocation
import Foundation
import UIKit

class MapViewController: UIViewController, MKMapViewDelegate {

...

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myPin")

pinAnnotationView.pinColor = .Purple
pinAnnotationView.draggable = true
pinAnnotationView.canShowCallout = true
pinAnnotationView.animatesDrop = true

let deleteButton = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
deleteButton.frame.size.width = 44
deleteButton.frame.size.height = 44
deleteButton.backgroundColor = UIColor.redColor()
deleteButton.setImage(UIImage(named: "trash"), forState: .Normal)

pinAnnotationView.leftCalloutAccessoryView = deleteButton

return pinAnnotationView
}

return nil
}

func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if let annotation = view.annotation as? PinAnnotation {
mapView.removeAnnotation(annotation)
}
}

That gives you something like this:

Sample Image

To add a new annotation to your map use this somewhere in your code:

let pinAnnotation = PinAnnotation()
pinAnnotation.setCoordinate(location)

mapView.addAnnotation(pinAnnotation)

MKMapView MKPointAnnotation tap event not called

Okay I found the solution by myself.

You have to set the annotationViews frame explicitly. If you just set subViews for the view, they will be shown, but the views frame is set to 0, 0 (height, weight). So you just can't tap it, cause the area is 0, 0 as well.

My solution is this, the interesting line is annotationView.frame = CGRect(x: 0, y: 0, width: 67, height: 26). Everything else is the same. Now a click on a annotation calls didSelectAnnotationView.

func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if (annotation is MKUserLocation) {
return nil
}

let reuseId = "stationAnnotation"

let annoStation = DataManager.getStationById(annotation.title!.toInt()!)

var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if annotationView == nil {

annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
annotationView.frame = CGRect(x: 0, y: 0, width: 67, height: 26)
let base = UIView(frame: CGRect(x: 0, y: 0, width: 67, height: 26))
base.userInteractionEnabled = true

let imageView = UIImageView(frame: CGRect(x: 2, y: 2, width: 22, height: 22))
imageView.image = UIImage(named: "zapfsaeule")

base.layer.cornerRadius = 3.0
base.clipsToBounds = true
base.backgroundColor = UIColor.whiteColor()

var priceLabelBig = UILabel(frame: CGRect(x: 25, y: 0, width: 30, height: 25))
priceLabelBig.textColor = UIColor.blackColor()
priceLabelBig.font = UIFont(name: priceLabelBig.font.fontName, size: 15)

var priceLabelSmall = UILabel(frame: CGRect(x: 55, y: 0, width: 12, height: 15))
priceLabelSmall.textColor = UIColor.blackColor()
priceLabelSmall.font = UIFont(name: priceLabelBig.font.fontName, size: 12)

if let curPrice = annoStation?.getPriceWithSettings().description {
var stringLength = countElements(curPrice)
var substringToIndex = stringLength - 1
priceLabelBig.text = curPrice.substringToIndex(advance(curPrice.startIndex, substringToIndex))
priceLabelSmall.text = curPrice.substringFromIndex(advance(curPrice.startIndex, substringToIndex))
}

base.addSubview(imageView)
base.addSubview(priceLabelBig)
base.addSubview(priceLabelSmall)

annotationView.addSubview(base)
annotationView.annotation = annotation
}
else {
annotationView.annotation = annotation
}

return annotationView
}

How to call a function or tell if a MKPointAnnotation is clicked on a MKMapView SwiftUI

Assuming that you're wrapping your MKMapView inside a UIViewRepresentable struct, add a coordinator with the MKMapViewDelegate protocol to listen for changes on your map:

//Inside your UIViewRepresentable struct
func makeCoordinator() -> Coordinator {
Coordinator()
}

class Coordinator: NSObject, MKMapViewDelegate {
//Delegate function to listen for annotation selection on your map
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let annotation = view.annotation {
//Process your annotation here
}
}
}

There are a couple of tutorials out there on how to include an MKMapView in SwiftUI and use delegation to access the MKMapViewDelegate functions through UIViewRepresentable and coordinators.

Following along my suggestion, your previous code would look like so:

struct MapKitView: UIViewRepresentable {

typealias Context = UIViewRepresentableContext<MapKitView>

func makeUIView(context: Context) -> MKMapView {
let map = MKMapView()
map.delegate = context.coordinator
let annotation = MKPointAnnotation()

annotation.coordinate = donator.coordinates
annotation.title = donator.name
annotation.subtitle = donator.car
map.addAnnotation(annotation)
return map
}

//Coordinator code
func makeCoordinator() -> Coordinator { ... }
}

Get annotation pin tap event MapKit Swift

You should create a custom annotation class, like:

class EventAnnotation : MKPointAnnotation {
var myEvent:Event?
}

Then, when you add your annotations, you'll link the Event with the custom annotation:

for event in events {
let eventpins = EventAnnotation()
eventpins.myEvent = event // Here we link the event with the annotation
eventpins.title = event.eventName
eventpins.coordinate = CLLocationCoordinate2D(latitude: event.eventLat, longitude: event.eventLon)
mapView.addAnnotation(eventpins)
}

Now, you can access the event in the delegate function:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
// first ensure that it really is an EventAnnotation:
if let eventAnnotation = view.annotation as? EventAnnotation {
let theEvent = eventAnnotation.myEvent
// now do somthing with your event
}
}


Related Topics



Leave a reply



Submit