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

后端 未结 8 813
梦谈多话
梦谈多话 2020-12-23 02:09

When displaying directions on the built-in Maps.app on the iPhone you can \"select\" one of the usually 3 route alternatives that are displayed by tapping on it. I wan\'t to

相关标签:
8条回答
  • 2020-12-23 03:02

    The question is rather old, but my answer may be useful to other people looking for a solution for this problem.

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

    /** Returns the distance of |pt| to |poly| in meters
     *
     * from http://paulbourke.net/geometry/pointlineplane/DistancePoint.java
     *
     */
    - (double)distanceOfPoint:(MKMapPoint)pt toPoly:(MKPolyline *)poly
    {
        double distance = MAXFLOAT;
        for (int n = 0; n < poly.pointCount - 1; n++) {
    
            MKMapPoint ptA = poly.points[n];
            MKMapPoint ptB = poly.points[n + 1];
    
            double xDelta = ptB.x - ptA.x;
            double yDelta = ptB.y - ptA.y;
    
            if (xDelta == 0.0 && yDelta == 0.0) {
    
                // Points must not be equal
                continue;
            }
    
            double u = ((pt.x - ptA.x) * xDelta + (pt.y - ptA.y) * yDelta) / (xDelta * xDelta + yDelta * yDelta);
            MKMapPoint ptClosest;
            if (u < 0.0) {
    
                ptClosest = ptA;
            }
            else if (u > 1.0) {
    
                ptClosest = ptB;
            }
            else {
    
                ptClosest = MKMapPointMake(ptA.x + u * xDelta, ptA.y + u * yDelta);
            }
    
            distance = MIN(distance, MKMetersBetweenMapPoints(ptClosest, pt));
        }
    
        return distance;
    }
    
    
    /** Converts |px| to meters at location |pt| */
    - (double)metersFromPixel:(NSUInteger)px atPoint:(CGPoint)pt
    {
        CGPoint ptB = CGPointMake(pt.x + px, pt.y);
    
        CLLocationCoordinate2D coordA = [mapView convertPoint:pt toCoordinateFromView:mapView];
        CLLocationCoordinate2D coordB = [mapView convertPoint:ptB toCoordinateFromView:mapView];
    
        return MKMetersBetweenMapPoints(MKMapPointForCoordinate(coordA), MKMapPointForCoordinate(coordB));
    }
    
    
    #define MAX_DISTANCE_PX 22.0f
    - (void)handleTap:(UITapGestureRecognizer *)tap
    {
        if ((tap.state & UIGestureRecognizerStateRecognized) == UIGestureRecognizerStateRecognized) {
    
            // Get map coordinate from touch point
            CGPoint touchPt = [tap locationInView:mapView];
            CLLocationCoordinate2D coord = [mapView convertPoint:touchPt toCoordinateFromView:mapView];
    
            double maxMeters = [self metersFromPixel:MAX_DISTANCE_PX atPoint:touchPt];
    
            float nearestDistance = MAXFLOAT;
            MKPolyline *nearestPoly = nil;
    
            // for every overlay ...
            for (id <MKOverlay> overlay in mapView.overlays) {
    
                // .. if MKPolyline ...
                if ([overlay isKindOfClass:[MKPolyline class]]) {
    
                    // ... get the distance ...
                    float distance = [self distanceOfPoint:MKMapPointForCoordinate(coord)
                                                    toPoly:overlay];
    
                    // ... and find the nearest one
                    if (distance < nearestDistance) {
    
                        nearestDistance = distance;
                        nearestPoly = overlay;
                    }
                }
            }
    
            if (nearestDistance <= maxMeters) {
    
                NSLog(@"Touched poly: %@\n"
                       "    distance: %f", nearestPoly, nearestDistance);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-23 03:09

    Updated for Swift 3

    func isTappedOnPolygon(with tapGesture:UITapGestureRecognizer, on mapView: MKMapView) -> Bool {
        let tappedMapView = tapGesture.view
        let tappedPoint = tapGesture.location(in: tappedMapView)
        let tappedCoordinates = mapView.convert(tappedPoint, toCoordinateFrom: tappedMapView)
        let point:MKMapPoint = MKMapPointForCoordinate(tappedCoordinates)
    
        let overlays = mapView.overlays.filter { o in
            o is MKPolygon
        }
    
        for overlay in overlays {
            let polygonRenderer = MKPolygonRenderer(overlay: overlay)
            let datPoint = polygonRenderer.point(for: point)
            polygonRenderer.invalidatePath()
    
            return polygonRenderer.path.contains(datPoint)
        }
        return false
    }
    
    0 讨论(0)
提交回复
热议问题