UIView's -hitTest:withEvent: called three times?
I am trying to intercept any activity (i.e. touches) that happens inside my whole application.
In other words, I am trying to be notified of any touch event that happens within my main UIView, containing the rest of my controls. To do so, I thought the UIView's method -hitTest:withEvent: was a good solution.
However, when I NSLog into this overriden method before call开发者_JAVA技巧ing [super hitTest:... withEvent:...], I see that it is called 3 times for any touch I make, and I cannot see any difference in the event I receive each time it is called.
Here is how is implemented the method in the main view of my application :
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(@"hitTest:withEvent called :");
NSLog(@"Event: %@", event);
NSLog(@"Point: %@", NSStringFromCGPoint(point));
NSLog(@"Event Type: %d", event.type);
NSLog(@"Event SubType: %d", event.subtype);
NSLog(@"---");
return [super hitTest:point withEvent:event];
}
And here is what I NSLog for a single touch in this view :
2010-11-29 14:09:26.892 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.892 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.892 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.892 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.892 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.893 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37935.2 touches: {(
)}
2010-11-29 14:09:26.893 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.893 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.893 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.893 Application[68818:207] ---
2010-11-29 14:09:26.893 Application[68818:207] hitTest:withEvent called :
2010-11-29 14:09:26.894 Application[68818:207] Event: <UITouchesEvent: 0x5716d60> timestamp: 37944.9 touches: {(
)}
2010-11-29 14:09:26.894 Application[68818:207] Point: {173, 498}
2010-11-29 14:09:26.894 Application[68818:207] Event Type: 0
2010-11-29 14:09:26.894 Application[68818:207] Event SubType: 0
2010-11-29 14:09:26.894 Application[68818:207] ---
How could I make any difference between those three notifications in order to trigger the action I want to make only one time for a single touch ?
Thanks in advance !
There are indeed 3 calls to hitTest. It is not clear why, but we can surmise by the timestamps on the event that the first two calls are to do with completing the previous gesture - those timestamps are always very close to whenever the previous touch happened, and will be some distance from the current time.
This is how I determine which hitTest to process:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
NSTimeInterval system = [[NSProcessInfo processInfo] systemUptime];
if (system - event.timestamp > 0.1) {
// not the event we were interested in
} else {
// use this call
}
}
If this is still a problem for you.
I found several examples and discussions about this topic but the proper solution is quite simple.
In general hittest is called three times within a UIView or a UIScrollView - this results of traversing the view-hierarchy.
A quite simple and for me always suitable solution is:
implement the function hittest in the view you implemented
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
for me - all three function calls have always the same position - so just store the location in your local class.
in the .h File:
@interface MyScrollView : UIScrollView {
CGPoint touchLocation_;
}
@property (nonatomic, readonly) CGPoint touchLocation;
in the .m File
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
return [super hitTest:point withEvent:event];
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
// The point check you need
if(point.y != self.touchLocation.y){
touchLocation_ = point;
// The function call you need - just called once.
}
return [super pointInside:point withEvent:event];
}
This solution works for me in several projects quite well.
The number of event responses you receive depends on the view hierarchy.
This method traverses the view hierarchy by sending the pointInside:withEvent: message to each subview to determine which subview should receive a touch event. If pointInside:withEvent: returns YES, then the subview’s hierarchy is traversed; otherwise, its branch of the view hierarchy is ignored. You rarely need to call this method yourself, but you might override it to hide touch events from subviews.
This method ignores view objects that are hidden, that have disabled user interaction, or have an alpha level less than 0.01. This method does not take the view’s content into account when determining a hit. Thus, a view can still be returned even if the specified point is in a transparent portion of that view’s content.
Points that lie outside the receiver’s bounds are never reported as hits, even if they actually lie within one of the receiver’s subviews. This can occur if the current view’s clipsToBounds property is set to NO and the affected subview extends beyond the view’s bounds.
From the UIView Class Reference.
In a nutshell, if the view you touch has three subviews, and those views are visible and within the bounds of their superview and touch region, you will receive three hit test responses.
This question has been asked for years, but I want to answer it:
The system calls hitTest and pointInside several times to fix the position, and each time the call stack is different, please refer to the official reply from apple.
精彩评论