29

iPhone に組み込まれている Maps.app で道順を表示する場合、通常 3 つのルートの選択肢の 1 つをタップして「選択」できます。この機能を複製して、タップが特定の MKPolyline 内にあるかどうかを確認したくありません。

現在、次のように MapView でタップを検出します。

// Add Gesture Recognizer to MapView to detect taps
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleMapTap:)];

// we require all gesture recognizer except other single-tap gesture recognizers to fail
for (UIGestureRecognizer *gesture in self.gestureRecognizers) {
    if ([gesture isKindOfClass:[UITapGestureRecognizer class]]) {
        UITapGestureRecognizer *systemTap = (UITapGestureRecognizer *)gesture;

        if (systemTap.numberOfTapsRequired > 1) {
            [tap requireGestureRecognizerToFail:systemTap];
        }
    } else {
        [tap requireGestureRecognizerToFail:gesture];
    }
}

[self addGestureRecognizer:tap];

私は次のようにタップを処理します。

- (void)handleMapTap:(UITapGestureRecognizer *)tap {
    if ((tap.state & UIGestureRecognizerStateRecognized) == UIGestureRecognizerStateRecognized) {
        // Check if the overlay got tapped
        if (overlayView != nil) {
            // Get view frame rect in the mapView's coordinate system
            CGRect viewFrameInMapView = [overlayView.superview convertRect:overlayView.frame toView:self];
            // Get touch point in the mapView's coordinate system
            CGPoint point = [tap locationInView:self];

            // Check if the touch is within the view bounds
            if (CGRectContainsPoint(viewFrameInMapView, point)) {
                [overlayView handleTapAtPoint:[tap locationInView:self.directionsOverlayView]];
            }
        }
    }
}

これは期待どおりに機能します。タップが指定された MKPolyline overlayView 内にあるかどうかを確認する必要があります (厳密ではありません。ユーザーがポリラインの近くのどこかをタップすると、これはヒットとして処理されます)。

これを行う良い方法は何ですか?

- (void)handleTapAtPoint:(CGPoint)point {
    MKPolyline *polyline = self.polyline;

    // TODO: detect if point lies withing polyline with some margin
}

ありがとう!

4

8 に答える 8

2

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
}
于 2016-12-09T08:12:02.683 に答える
2

Jensemann によって以下に提案されたソリューションはうまく機能しています。IOS 8 および 9 (XCode 7.1) で正常にテストされた、Swift 2 に適合した以下のコードを参照してください。

func didTapMap(gestureRecognizer: UIGestureRecognizer) {
    tapPoint = gestureRecognizer.locationInView(mapView)
    NSLog("tapPoint = %f,%f",tapPoint.x, tapPoint.y)
    //convert screen CGPoint tapPoint to CLLocationCoordinate2D...
    let tapCoordinate = mapView.convertPoint(tapPoint, toCoordinateFromView: mapView)
    let tapMapPoint = MKMapPointForCoordinate(tapCoordinate)
    print("tap coordinates = \(tapCoordinate)")
    print("tap map point = \(tapMapPoint)")

    // Now we test to see if one of the overlay MKPolyline paths were tapped
    var nearestDistance = Double(MAXFLOAT)
    let minDistance = 2000      // in meters, adjust as needed
    var nearestPoly = MKPolyline()
    // arrayPolyline below is an array of MKPolyline overlaid on the mapView
    for poly in arrayPolyline {                
        // ... get the distance ...
        let distance = distanceOfPoint(tapMapPoint, poly: poly)
        print("distance = \(distance)")
        // ... and find the nearest one
        if (distance < nearestDistance) {
            nearestDistance = distance
            nearestPoly = poly
        }
    }
    if (nearestDistance <= minDistance) {
        NSLog("Touched poly: %@\n    distance: %f", nearestPoly, nearestDistance);
    }
}


func distanceOfPoint(pt: MKMapPoint, poly: MKPolyline) -> Double {
    var distance: Double = Double(MAXFLOAT)
    var linePoints: [MKMapPoint] = []
    var polyPoints = UnsafeMutablePointer<MKMapPoint>.alloc(poly.pointCount)
    for point in UnsafeBufferPointer(start: poly.points(), count: poly.pointCount) {
        linePoints.append(point)
        print("point: \(point.x),\(point.y)")
    }
    for n in 0...linePoints.count - 2 {
        let ptA = linePoints[n]
        let ptB = linePoints[n+1]
        let xDelta = ptB.x - ptA.x
        let yDelta = ptB.y - ptA.y
        if (xDelta == 0.0 && yDelta == 0.0) {
            // Points must not be equal
            continue
        }
        let u: Double = ((pt.x - ptA.x) * xDelta + (pt.y - ptA.y) * yDelta) / (xDelta * xDelta + yDelta * yDelta)
        var ptClosest = MKMapPoint()
        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
}
于 2015-12-23T16:04:06.873 に答える