3
votes

`I've got view with a UIImageView as a subview. The main view has a single tap GestureRecognizer on it that works 99% of the time.

Every once in a while, the gesture recognizer seems to fail, and the event gets pushed up the stack to the next responder. I've added a breakpoint in the hitTest method of that top view, and the hit test method correctly returns the inner-most UIImageView in all cases, including when the event goes to the wrong place.

In what situation could a view be the result of a hitTest but not get the event passed to its gesture recognizer? I've printed out this debug code from the hitTest breakpoint when the gesture selector doesn't get called:

(lldb) po u
$2 = 0x20854520 <UIImageView: 0x20854520; frame = (0 0; 61 62); opaque = NO; layer = <CALayer: 0x208e7670>>

(lldb) po [u superview]
$0 = 0x1fd225e0 <HotspotView: 0x1fd225e0; frame = (303 663; 61 62); gestureRecognizers = <NSArray: 0x2082b850>; layer = <CALayer: 0x1fd15af0>>

(lldb) po ((HotspotView *)u.superview).gestureRecognizers
$1 = 0x208bc940 <__NSArrayI 0x208bc940>(
    <UITapGestureRecognizer: 0x20828fa0; state = Possible; cancelsTouchesInView = NO; delaysTouchesEnded = NO; view = <HotspotView 0x1fd225e0>; target= <(action=handleSingleTap:, target=<HotspotView 0x1fd225e0>)>>
)

u is the result of the hit test (the UIImageView). u.superview is the container view that has the gesture recognizer attached to it. So, here, I confirm that u.superview has a gesture recognizer. Shouldn't it, then, receive that event? In what situation would this event not make it into singleTap and instead bubble up to the next responder?

edit: I've managed to capture the problem again (XCode crashed last time I had it after a few minutes...) and now I've found that the touch even makes it all the way into the HotspotView. touchesBegan gets the touch correctly even if it then fails to call the gesture recognizer. I've logged the touch and touch events for both successful and unsuccessful taps (in terms of whether they call the GR or not) and they appear to be identical.

So, now I'm even more lost.

1) The right object is returned for the hit test
2) The right object receives its touchesBegan call
3) The object has its gesture recognizer
4) And yet, sometimes, that gesture recognizer is not called (with no reliability and very difficult reproduction).

1
It looks like everything is fine. What does "bubble up to the next responder" mean?matt
If you goal is to make the image view tappable, why don't you attach a gesture recognizer to the image view?matt
Did you set the userInteractionEnabled of the image view? I presume you did, since it is the result of a hit test.matt
The image view size doesn't necessarily match the hittable area size, hence them being two different objects. And yes, userInteractionEnabled is obviously set to YES, else it wouldn't ever work.puzzl

1 Answers

1
votes

My guess is that in the situations where the gesture recognizer does not recognize, the gesture was not a qualifying tap. If you accidentally tap twice in succession, for example, or hold down for too long, or slide your finger a little, the gesture might not seem like a single tap to the gesture recognizer.

In other words, keep in mind that a touch is not a gesture. You are doing some nice logging that shows what the structure is, and you've shown that the most recent touch was on the UIImageView, but that does not mean that the gesture recognizer recognized this as a tap. There might be other conflicting gesture recognizers, or the gesture might not seem like a tap.

By the way, you are doing a very odd thing here. HotspotView is exactly the same size as the UIImageView. So it seems like its only purpose is to detect the tap. But you could have done that with the UIImageView alone. Why not keep it simple? Set the userInteractionEnabled of the UIImageView and attach the tap gesture recognizer to the UIImageView itself.