Search code examples
iosswiftmkannotationview

Annotation not responding to hit even though overridden hittest is returning correct value


I have searched and tried all manner of solutions and the code below is what I've come up with so far. I've tried so many tips the code is untidy but I hope I have stripped out most of this.

I have a custom annotation with a banner that falls outside the bounds of the annotation itself. I want to respond to hits on the banner.

Using the code below, when I click on the banner I get this output:

AV returning hit view: <UILabel: 0x7fd70bd8f4e0; frame = (0 -10; 77 12); text = 'Coffee Republic'; layer = <_UILabelLayer: 0x60000008e920>> as <GJ3.AnnotationView: 0x7fd70f3035e0; frame = (112 349.5; 0 0); layer = <CALayer: 0x618000a37ac0>>

I have also set the code to return hit view!.superview! and the log above shows that would return the annotation.

When I click the annotation area itself, I get the callout I expect. Clicking on the banner produces no response. I did notice when I click on the actual annotation area, the logs only show 'nil' returns, I don't see why that works!

My specific question is, is the return I'm sending from hitTest correct, should it work? If it is, what could be preventing it?

class AnnotationView: MKAnnotationView {
var companyName: String!
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

    let hitView = super.hitTest(point, with: event)

    if (hitView != nil) {
        self.superview?.bringSubview(toFront: self)
    }

    if (hitView != nil) {
        print ("AV returning hit view: \(hitView!) as \(hitView!.superview!)")
        return hitView!
    } else {
        print ("AV returning hit view: nil")
        return nil
    }
}

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {

    let rect = self.bounds
    var isInside = rect.contains(point)

    if(!isInside) {
        for view in self.subviews as [UIView] {
            print ("AV view: \(view)")
            if !view.isHidden && view.alpha > 0 && view.isUserInteractionEnabled && view.point(inside: convert(point, to: view), with: event) {
                print ("hit in \(view)")
                isInside = true
                return isInside
            }
        }
    }
    return isInside
}

}


Solution

  • I would advise against modifying the hitTest and instead increase the size of the annotation itself (split down the annotation into a container view and the banner into another view that slides in after but still within the main MKAnnotationView). Modifying the responder chain like that can lead to some undesirable behaviours in future OS releases.

    I would suggest reading up on the responder chain to understand exactly whats going on if you still want to head that direction (a good recent post here: https://medium.com/bpxl-craft/event-delivery-on-ios-part-1-8e68b3a3f423#.og2cpaeb0).