3
votes

How should I be handling, or rather NOT handling (ignoring), touches to my background view? It happens to be the view of my View Controller which has subviews (objects) that I DO want to respond to touch events. Setting userInteractionEnabled = NO for the view seems to turn off ALL interaction for the subviews as well.

I'm currently testing for

if ([[touch view] superview] == self.view) 

in touchesBegan/Moved/Ended. But I'm trying to eliminate some conditional testing so looking for a better way...

2

2 Answers

2
votes

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; recursively calls -pointInside:withEvent:. point is in frame coordinates

If you override that in your background view, you can specify which subview gets hit - you can be lazy and just ask each of your subviews if they would return yes to it, and never return yourself (return nil if they all return nil). Something like:

UIView *hitView = nil;
NSArray *subviews = [self subviews];
int subviewIndex, subviewCount = [subviews count];
for (int subviewIndex = 0; !hitView && subviewIndex < subviewCount; subviewIndex++) {
    hitView = [[subviews objectAtIndex:subviewIndex] hitTest:point withEvent:event];
}
return hitView;
2
votes

Thanks for the answer Dan, great question too.

However the accepted answer has an error: hitTesting subviews should be done with the point converted to the subview. Also, subviewIndex is already defined before 'for'.

Since subviews are ordered according to their z index, iterating should go from the last to the first one (see Event handling for iOS - how hitTest:withEvent: and pointInside:withEvent: are related? and How to get UIView hierarchy index ??? (i.e. the depth in between the other subviews)).

Here's the updated code:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *hitView = nil;
    NSArray *subviews = [self subviews];
    int subviewIndex, subviewCount = [subviews count];
    for (subviewIndex = subviewCount-1; !hitView && subviewIndex >= 0; subviewIndex--) {
        UIView *subview = [subviews objectAtIndex:subviewIndex];
        hitView = [subview hitTest:[self convertPoint:point toView:subview] withEvent:event];
    }
    return hitView;
}