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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…