Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Callout from MKPolylineView?

Is there any way to show a callout when a person touch a MKPolylineView? i tried to add a UITapGestureRecognizer and in selector display a callout in some coordinate. but didnt work. any suggestions for making this?

the following is what i tried in method

 - (MKOverlayView *)mapView:(MKMapView *)mapa viewForOverlay:(id <MKOverlay>)overlay
        self.polylineView = [[MKPolylineView alloc] initWithPolyline: self.polyline];
        self.polylineView.strokeColor = [UIColor blackColor];
        self.polylineView.lineWidth = 5.0;
        self.polylineView.alpha = 0.7;
        UITapGestureRecognizer *touchOnView = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(addBubble:)];
        [touchOnView setNumberOfTapsRequired:1];
        [touchOnView setNumberOfTouchesRequired:1];
        [self.polylineView addGestureRecognizer:touchOnView];
like image 448
47tucanae Avatar asked Feb 21 '23 00:02

47tucanae


1 Answers

Very interesting question - I'd never even thought of putting a gesture recognizer on a map overlay. With a bit of experimentation, I've verified it is possible to detect a tap on the MKPolylineView. Just as you found, the tap gestures don't work on map overlays. So instead I put the tap gesture on the MKMapView rather than on the MKPolylineView. Then to handle the tap:

- (void)handleTapGesture:(UIGestureRecognizer*)gestureRecognizer
{
    if (measureLine != nil)
    {
        UIView* hitView = [self.polylineView hitTest:[gestureRecognizer locationInView:self.polylineView] withEvent:nil];
    }
}

hitView will be nil if your tap was outside of the MKPolylineView, or it will be self.polylineView if the tap was inside.

However, this might not behave quite as you'd like. For horizontal and vertical lines, it works perfectly because the size of the underlying view is roughly the same size as the line. But for a 45 degree line, the underlying view has to be much larger than than the line, because it is an axis aligned bounding box (AABB). If you think about a 45 degree line, to enclose it using only horizontal and vertical lines you will end up with a large area - much larger than you'd want to detect taps in.

e.g.

--------
|    / |
|   /  |
|  /   |
| /    |
|/     |
--------

But using a tap gesture or the hit test will always result in recognizing taps inside these AABBs. So regardless of where you try to attach your gesture - e.g. to the MKPolylineView as you tried, or to the MKMapView, you'll get spurious results. The problem gets worse for longer lines - if you imagine a line going from the top right of your map view to the bottom left, the AABB that you'd need to enclose it would cover the entire area of the map view, meaning that a tap in the top left or bottom right would be interpreted as hitting the MKPolylineView.

To solve the problem, I'd suggest the following approach:

  • Use a tap gesture recognizer on the map view
  • In your method that handles the tap:
    • convert the screen tap position to a map coordinate
    • loop through each of your polylines (unless you have only 1)
    • for each point in the polyline take the line segment that connects that point to the next point, and calculate the distance of your map coordinate away from this line. Use trigonometry to calculate this.
      • if the distance is very close to this segment, then stop checking the rest of the segments and handle whatever callouts etc you wanted to show
      • if the distance is not close, then move onto the next point and the line segment that connects it to the next but one point

This approach is guaranteed to work regardless of the length of your polylines, or what angle they are at. There are no AABB concerns. The downside is that all of those distance calculations might be computationally expensive - so if your polylines are made up of a great number of points, or if you have a large number of polylines, then you might not be able to do all of those calculations without blocking the UI from being responsive, meaning you'd need to move it into a background thread. If you only have a handful of polylines, and/or they are made up of few points, then you'll be fine.

like image 86
Rob B Avatar answered Mar 06 '23 18:03

Rob B