user1898829
user1898829

Reputation: 3527

Detecting touches on mkoverlay

I'm drawing overlays based on directions. I draw from route a to route b and then route b to route c.

I would like to detect if the overlay is tapped anywhere on mkoverlay.

I used this example Detecting touches on MKOverlay in iOS7 (MKOverlayRenderer)

and converted it to swift.

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

    if let touch = touches.first {
        if touch.tapCount == 1 {
            let touchLocation = touch.location(in: self)
            let locationCoordinate = self.convert(touchLocation, toCoordinateFrom: self)

            let mapPoint: MKMapPoint = MKMapPointForCoordinate(locationCoordinate)
            let mapPointAsCGP = CGPoint(x: CGFloat(mapPoint.x), y: CGFloat(mapPoint.y))
            for overlay: MKOverlay in self.overlays {
                if (overlay is MKPolygon) {
                    let polygon: MKPolygon? = (overlay as? MKPolygon)
                    let mpr: CGMutablePath = CGMutablePath()
                    let polygonPoints = polygon?.points
                    let polygonPointCount = Int((polygon?.pointCount)!)
                    for p in 0..<polygonPointCount {
                        let mp: MKMapPoint = polygonPoints.unsafelyUnwrapped()[polygonPointCount]
                        if p == 0 {
                            mpr.move(to: CGPoint(x: CGFloat(mp.x), y: CGFloat(mp.y)), transform: .identity)
                        }
                        else {
                            mpr.addLine(to: CGPoint(x: CGFloat(mp.x), y: CGFloat(mp.y)), transform: .identity)
                        }
                    }

                    if mpr.contains(mapPointAsCGP, using: .winding, transform: .identity) {
                        print("test")
                    }
                }
            }

        }
    }

    super.touchesEnded(touches, with: event)
}

I'm not sure why they have the if statement there

if (overlay is MKPolygon)  

because this would never be called since its an array of mkoverlay.

Upvotes: 1

Views: 1905

Answers (1)

Tristan Beaton
Tristan Beaton

Reputation: 1762

I found that doing it this way is a lot better than relying on the touch event methods.

For your question about:

if (overlay is MKPolygon)

We need to cast the overlay to a polygon so we can get access to the points array, and rebuild the path. Although in my method below, you'll only need access to the points array is the renderer.path is nil. Since I'm using the overlays that are currently on the map, I'm confident that the path is not nil.

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    let tap = UITapGestureRecognizer(target: self, action: #selector(mapTapped(_:)))

    self.map.addGestureRecognizer(tap)
}

func mapTapped(_ gesture: UITapGestureRecognizer){

    let point = gesture.location(in: self.map)

    let coordinate = self.map.convert(point, toCoordinateFrom: nil)

    let mappoint = MKMapPointForCoordinate(coordinate)

    for overlay in self.map.overlays {

        if let polygon = overlay as? MKPolygon {

            guard let renderer = self.map.renderer(for: polygon) as? MKPolygonRenderer else { continue }

            let tapPoint = renderer.point(for: mappoint)

            if renderer.path.contains(tapPoint) {

                print("Tap was inside this polygon")

                break // If you have overlapping overlays then you'll need an array of overlays which the touch is in, so remove this line.
            }

            continue
        }

        if let circle = overlay as? MKCircle {

            let centerMP = MKMapPointForCoordinate(circle.coordinate)

            let distance = MKMetersBetweenMapPoints(mappoint, centerMP) // distance between the touch point and the center of the circle

            if distance <= circle.radius {

                print("Tap was inside this circle")

                break // If you have overlapping overlays then you'll need an array of overlays which the touch is in, so remove this line.
            }

            continue
        }
    }
}

Upvotes: 6

Related Questions