Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView hitTest:withEvent: calls come in threes

I have a UIScrollView with no subviews. When I drag the scroll view, its hitTest:withEvent: gets called three times, and there are never any touches in the event. Why is that (I saw another post but it doesn't seem to have a consclusion)? Here is the log of the scroll view's hitTest... and pointInside:withEvent:

2011-05-11 20:12:37.472 MyApp[10909:707] hit test for UIScrollView 119.500000,102.000000, timestamp: 357978 touches: {()}
2011-05-11 20:12:37.475 MyApp[10909:707] pointInside for UIScrollView 119.500000,102.000000,  timestamp: 357978 touches: {()} 
2011-05-11 20:12:37.477 MyApp[10909:707] hit test for UIScrollView 119.500000,102.000000, timestamp: 357978 touches: {()}
2011-05-11 20:12:37.479 MyApp[10909:707] pointInside for UIScrollView 119.500000,102.000000,  timestamp: 357978 touches: {()} 
2011-05-11 20:12:37.481 MyApp[10909:707] hit test for UIScrollView 119.500000,102.000000, timestamp: 358021 touches: {()}
2011-05-11 20:12:37.482 MyApp[10909:707] pointInside for UIScrollView 119.500000,102.000000,  timestamp: 358021 touches: {()} 
2011-05-11 20:12:37.484 MyApp[10909:707] pointInside for UIScrollView 119.500000,396.000000,  timestamp: 358021 touches: {()} 
like image 884
prostock Avatar asked May 12 '11 03:05

prostock


3 Answers

hitTest: is a utility method designed to find a view at a specific point. It DOES NOT represent a user tapping on the touch screen. It is totally sensible for hitTest to be called several times in response to the same event; all the method is supposed to do is return the view under the point and it SHOULD NOT trigger any side effects.

If you want to track touch events, you should override touchesBegan: and friends.

like image 125
benzado Avatar answered Nov 02 '22 15:11

benzado


I had the same problem and didn't actually find an answer to this, but you could use one of these 2 solutions:

1. Store the hit tests in an array and compare them so you can return when it's the same to prevent it getting called 3 times in a row.

2. Instead of hittest, use touchesBegan, touchesCancelled, touchesEnded and touchesMoved like this:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch * touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:touch.view];
    NSLog(@"%f - %f", touchLocation.x, touchLocation.y);
}

I used the touchesBegan, etc solution and it works like a charm :)

like image 37
Rick van der Linde Avatar answered Nov 02 '22 15:11

Rick van der Linde


I don't know for sure why, but here are some further insights into this question:

  • It doesn't seem to be specific to UIScrollView. I ran a test with a UIView - same results.
  • It seems to get called three times on the root view regardless of the view hierarchy or the return value.
  • The event passed to hitTest is basically blank for the first two calls (but the point is always valid); the event has a valid timestamp but no touch information for the third call.
  • It looks like UIResponder methods aren't called until after all three hitTest calls complete. For example, touchesBegan:withEvent: won't be called until the end.
  • All three calls originate from different points in the (closed-source, I believe) function UIApplicationHandleEvent, based on stack traces.

My best guess is that, in some cases, the return value from hitTest might change between calls. I cannot think of why this might happen, though. If this isn't true, then it doesn't make sense to call it multiple times.

Another idea is that the code is simply inefficiently-written. This seems less likely, but based on this information, it is a possibility.

like image 31
Tyler Avatar answered Nov 02 '22 15:11

Tyler