How to Make a Mkannotationview Touch Sensitive

How do I make a MKAnnotationView touch sensitive?

To do this, you need to subclass MKAnnotationView to create your own custom MKAnnotationView. In your subclass, override the following functions:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event
{
// Return YES if the point is inside an area you want to be touchable
}

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
// Return the deepest view that the point is inside of.
}

This allows interactive views (such as buttons, etc.) to be pressed. The default implementation in MKAnnotationView is not strict on pointInside and hitTest because it allows presses that are actually inside one annotation to get sent to a different annotation. It does this by figuring out the closest annotation center to the touch-point and sending the events to that annotation, this is so that close-together (overlapping) annotations don't block each other from being selected. However, in your case you probably want to block other annotations if the user is to select and drag the topmost annotation, so the above method is probably what you want, or else it will set you on the right path.

EDIT:
I was asked in comments for an example implementation of hitTest:withEvent: - This depends on exactly what you are trying to achieve. The original question suggested touching and dragging images within the annotation whereas in my case I have some buttons inside the annotation that I want to be interactive. Here's my code:

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
UIView* hitView = [super hitTest:point withEvent:event];
if (hitView != nil)
{
// ensure that the callout appears above all other views
[self.superview bringSubviewToFront:self];

// we tapped inside the callout
if (CGRectContainsPoint(self.resultView.callButton.frame, point))
{
hitView = self.resultView.callButton;
}
else if (CGRectContainsPoint(self.resultView.addButton.frame, point))
{
hitView = self.resultView.addButton;
}
else
{
hitView = self.resultView.viewDetailsButton;
}
[self preventSelectionChange];
}
return hitView;
}

As you can see it's quite simple - The MKAnnotationView implementation (called as super on the first line of my implementation) only returns the first (outermost) view, it does not drill down through the view hierarchy to see which sub-view the touch is actually inside. In my case I just check if the touch is inside one of three buttons and return those. In other circumstances you may have simple rectangle-based drilling down through the hierarchy or more complex hit tests for example to avoid transparent areas within your view to allow touches to pass through those parts. If you do need to drill down, CGRectContainsPoint can be used the same way I have used it, but remember to offset your points into local view coordinates for each view-level you drill into.

The preventSelectionChange method is to prevent my custom annotation from becoming selected, I am using it as a customisable/interactive callout from map pins and this keeps the pin it relates to selected instead of allowing the selection to change to this annotation.

Custom MKAnnotationView touch zone is ridiculously narrow

As far as I understood, func point(inside:with:) and hitTest(point:with:) do not help to enlarge the zone which call the delegate public func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)

Finally, I ended up by enlarging the MKAnnotationView frame to enclose my annotation subviews and moving the anchor point to be exactly where I wanted my annotation center

self.frame = CGRect(x: 0, y: 0, width: 
self.myLabel.frame.width, height: self.myLabel.frame.height)
self.centerOffset = CGPoint(x: self.frame.width/2, y: self.myImageView.frame.height/2)

Then I deleted point(inside:with:) and hitTest(point:with:) overrides that did nothing.

That's the only way I succeeded to make my annotation view fully reactive.

Prevent touch events on MKMapView being detected when a MKAnnotation is tapped

I think you will find the following links very useful:

http://blog.asynchrony.com/2010/09/building-custom-map-annotation-callouts-part-2/

How do I make a MKAnnotationView touch sensitive?

The first link discusses (among other things) how to prevent the propagation of touches to the annotations so that they selectively respond, and the second one how to detect the touches.

How to trigger MKAnnotationView's callout view without touching the pin?

Ok, here's the solution to this problem.

To display the callout use MKMapView's selectAnnotation:animated method.

Prevent touch propagation to annotation beneath

The default behavior of the touch handling methods (touchesBegan: touchesEnded: etc.) have this note in the documentation:

The default implementation of this method does nothing. However immediate UIKit subclasses of UIResponder, particularly UIView, forward the message up the responder chain.

MKAnnotationView is a subclass of UIVIew. As a result, when your annotation gets a touch, it is passing it to it's super class and on up the responder chain, so eventually you map view gets the touch and activates the covered annotation.

To resolve, implement the touch handling methods in your annotationView class, and do not pass the touch events up the responder chain.

Tapping an MKAnnotation to select it is REALLY slow

Unfortunately, there's nothing you can do about this. It's for the exact same reason that tapping links in Mobile Safari is slow: the gesture recognizers have to jostle for a while to decide whether you might be scrolling (dragging) before they agree that you are tapping.

So, it has nothing to do with the animation. It's just the nature of gesture recognition in this situation.



Related Topics



Leave a reply



Submit