How to identify if MKPointAnnotation has been pressed in Swift?
mapView:didSelect:
is a MKMapViewDelegate
method. If you do not set mapView.delegate = self
on your ViewController
this function will never get triggered.
Typically it would get set in ViewDidLoad
. before performing any other operations with the mapView. Changing your ViewDidLoad
to look like
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
displayCordinates()
}
should fix your issue. For more information on the protocol
/delegate
design pattern all over the apple frameworks, I suggest this swift article from the Swift Programming Guide.
More specifically to your case, check out all the other functionality/control you can bring with your implementation of MKMapView
on your ViewController
by checkout out the apple docs on MKMapViewDelegate
. This will cover things like monitoring when the map finishes loading, when it fails, when the user's location is updated, and more things you may want to increase the functionality of your app and provide a great user experience.
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 { ... }
}
How can I detect which annotation was selected in MapView
For that you can use selected Annotation
from didSelectAnnotationView
, then store that annotation to instance variable and after that used annotation in your Button
action method.
var selectedAnnotation: MKPointAnnotation?
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
self.selectedAnnotation = view.annotation as? MKPointAnnotation
}
func info(sender: UIButton) {
print(selectedAnnotation?.coordinate)
}
Edit: As of you have custom MKAnnotation
class you need to use that.
var selectedAnnotation: Islands?
func mapView(mapView: MKMapView, didSelectAnnotationView view: MKAnnotationView) {
self.selectedAnnotation = view.annotation as? Islands
}
Get MKPointAnnotation title
In the custom rightButtonTapped
method, an easy and reliable way to get a reference to the annotation that was tapped is to use the map view's selectedAnnotations
array:
func rightButtonTapped(sender: AnyObject) {
if self.mapView.selectedAnnotations?.count == 0 {
//no annotation selected
return;
}
if let ann = self.mapView.selectedAnnotations[0] as? MKAnnotation {
println("\(ann.title!)")
}
}
(Even though selectedAnnotations
is an array, the map view only allows one annotation to be "selected" at a time so the currently selected annotation is always at index 0.)
However, a better way than using a custom button method is to use the map view's calloutAccessoryControlTapped
delegate method. The delegate method passes you a reference to the annotation view that was tapped from which you can easily get the underlying annotation.
To use the delegate method, remove the addTarget
line for your custom method:
//Do NOT call addTarget if you want to use the calloutAccessoryControlTapped
//delegate method instead of a custom button method.
//rightButton.addTarget(self, action: "rightButtonTapped:", forControlEvents: UIControlEvents.TouchUpInside)
and then implement the delegate method instead of your custom button method:
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
println("\(view.annotation.title!)")
}
How to identify when a annotation is pressed which one it is
As I can see, you've already created a class subclassing a MKPointAnnotation
.
This is simple, you just create a variable with a name and type.
class CustomPointAnnotation: MKPointAnnotation {
var tag: Int!
}
Then in your code, set the variable.
var pin = CustomPointAnnotation()
pin.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
pin.title = "Bus Stop"
pin.subtitle = "City Stand D"
pin.imageName = "pin"
pin.tag = index
mapView.addAnnotation(pin)
Then to access the variable when you click on the annotation. Simply do it here.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let annotation = view.annotation as? CustomPointAnnotation {
print(annotation.tag!)
}
}
There you go.
If you have any issues shoot me a comment.
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.
Related Topics
Ckcontainer.Discoverallidentities Always Fails
Updating a @Published Variable Based on Changes in an Observed Variable
Why It Is Called the Memberwise Initialiser
Using Custom Cifilter on Calayer Shows No Change to Calayer
Programmatically Create an Nsviewcontroller Without an Xib in Swift 3
Getting an Issue with Upgrade to Xcode 10.2
How to Convert Curl Command to Swift
Glkit VS. Metal Perspective Matrix Difference
Reflection with Swift - Get Functions Name of a Class
What Is the Markup Format for Documentation on the Parameters of a Block in Swift
Swift Objc_Getassociatedobject Always Nil
Get Color of Point in a Skscene Swift
How to Make JSON Data Persistent for Offline Use (Swift 4)
Conform to Protocol and Keep Property Private
Refreshing Auth Token with Moya