How to Detect Taps on Mkpolylines/Overlays Like Maps.App

Issue with selecting alternate route in mapkit

Have you seen this answer yet?

Try setting a maximum polyLine width: 22px, for any zoom level.

“ This code detects touches on poly lines with a maximum distance of 22 pixels in every zoom level. Just point your UITapGestureRecognizer to handleTap:”

How to detect taps on MKPolylines/Overlays like Maps.app?

Detecting touches on MKOverlay in iOS7 (MKOverlayRenderer)

I've done it.

Thanks to incanus and Anna!

Basically I add a TapGestureRecognizer to the MapView, convert the point tapped to map coordinates, go through my overlays and check with CGPathContainsPoint.

Adding TapGestureRecognizer. I did that trick of adding a second double tap gesture, so that the single tap gesture isn't fired when doing a double tap to zoom on map. If anyone knows a better way, I'm glad to hear!

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapTap:)];
tap.cancelsTouchesInView = NO;
tap.numberOfTapsRequired = 1;

UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] init];
tap2.cancelsTouchesInView = NO;
tap2.numberOfTapsRequired = 2;

[self.mapView addGestureRecognizer:tap2];
[self.mapView addGestureRecognizer:tap];
[tap requireGestureRecognizerToFail:tap2]; // Ignore single tap if the user actually double taps

Then, on the tap handler:

-(void)handleMapTap:(UIGestureRecognizer*)tap{
CGPoint tapPoint = [tap locationInView:self.mapView];

CLLocationCoordinate2D tapCoord = [self.mapView convertPoint:tapPoint toCoordinateFromView:self.mapView];
MKMapPoint mapPoint = MKMapPointForCoordinate(tapCoord);
CGPoint mapPointAsCGP = CGPointMake(mapPoint.x, mapPoint.y);

for (id<MKOverlay> overlay in self.mapView.overlays) {
if([overlay isKindOfClass:[MKPolygon class]]){
MKPolygon *polygon = (MKPolygon*) overlay;

CGMutablePathRef mpr = CGPathCreateMutable();

MKMapPoint *polygonPoints = polygon.points;

for (int p=0; p < polygon.pointCount; p++){
MKMapPoint mp = polygonPoints[p];
if (p == 0)
CGPathMoveToPoint(mpr, NULL, mp.x, mp.y);
else
CGPathAddLineToPoint(mpr, NULL, mp.x, mp.y);
}

if(CGPathContainsPoint(mpr , NULL, mapPointAsCGP, FALSE)){
// ... found it!
}

CGPathRelease(mpr);
}
}
}

I could ask for the MKPolygonRenderer which already has the "path" property and use it, but for some reason it is always nil. I did read someone saying that I could call invalidatePath on the renderer and it does fill the path property but it just seems wrong as the point is never found inside any of the polygons. That is why I rebuild the path from the points. This way I don't even need the renderer and just make use of the MKPolygon object.

Touch events on MKMapView's overlays

Just in case it might help some of you...
I couldn't find a way to do that but I added an annotation on my overlays (Anyway, i needed to do that to display some information) and then I could get the touch event on this annotation. I know it is not the best way to do it but in my situation, and maybe yours, it works ;) !

How can I make map route instructions clickable on SwiftUI?

Yes, but you have to stop throwing away the data. MKRoute.Steps actually gives you all of the information you need, so first, set up RouteSteps to accept MKRoute.Steps:

struct RouteSteps: Identifiable {
let id = UUID()
let step: MKRoute.Step
}

Then, in your findNewLocation() function:

func findNewLocation(){
...
directions.calculate(completionHandler: { response, error in
// I used a guard statement to get rid of your force unwrap.
// NEVER force unwrap in a situation like this.
// Implement error handling here
guard let response = response else { return }
for route in response.routes {
// Map the MKRoute.Steps to your struct
self.routeSteps = route.steps.map( { RouteSteps(step: $0) } )
}
})
...

}

Then in the main part of your ContentView:

struct ContentView: View {
...

var body: some View {
...
List(routeSteps) { routeStep in
// Pull your instructions out here
Text(routeStep.step.instructions)
.onTapGesture {
// The MKRoute.Step contains an overlay polyline for MKMapKit.
// We can't use that in SwiftUI maps yet, but the polyline contains a
// boundingMapRect that we can use to change the region shown on the map
// with the MKCoordinateRegion(_ MKMapRect) initializer. Setting the region
// to the insturction's polyline boundingMapRect does exactly what you want.
region = MKCoordinateRegion(routeStep.step.polyline.boundingMapRect)
}
}
...
}

Alternative routes on MKDirections

Here are some "straightforward and easy" ways to do what you describe:

  1. "How to draw the alternative routes on a different color": Since MKDirections doesn't seem to use the subtitle property of the overlays it creates, you could stuff a "color code" in there before adding the overlay and then in rendererForOverlay, set the color of the polyline based on its subtitle.

    However, as @matt commented and answered, a better approach is to subclass MKPolyline and create an explicit color property.

  2. "Touch each route": Detecting a touch on a route is more difficult. This answer by @Jensemann works well.

  3. "Change colour when I touch each route": Changing the color once a route is touched can be done by removing that overlay, changing its "color code", then adding the overlay back. Another option here is to create a custom overlay view (renderer in iOS 7) whose color can be dynamically changed.

Polyline Tap in Google Maps

You can use isTappable property of GMSPolyline.

isTappable

If this overlay should cause tap notifications.

polyline.isTappable = true

Whenever the polyline is tapped, the GMSMapViewDelegate method didTapOverlay is called

func mapView(_ mapView: GMSMapView, didTap overlay: GMSOverlay) {
//Write your code here
}

For further information refer https://developers.google.com/maps/documentation/ios-sdk/reference/protocol_g_m_s_map_view_delegate-p.html#a3a2bf2ff4481528f931183cb364c0f4b



Related Topics



Leave a reply



Submit