I have two views each of which contain two subviews.
Hit detection is working fine as long as the two top views do not overlap. Hence I can touch subview marked A in the left side of the image below.
However as soon as the top two views overlap, there is just no way that the A view receives touches as view 1 is "above" view 2 and "eats" the touch.
Both View 1 and View 2 detect touches, as they can be moved around, hence need to detect and react to touches "in between" subviews.
This means that my two "top views" detection should say: "Oh, wait a minute, maybe I am ovelapping some other view and should pass the event to it, and only start a drag/move around if and only if no other view is 'below me'".
How would I do that?
Edit: Thanks jaydee3
This didn't work at first, resulting into an infinite recursion: each view deferring to its sibling, which in turn defers back to the initiating view:
- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView * hit = [super hitTest:point withEvent:event] ;
if (hit == self) {
for (UIView * sibling in self.superview.subviews) {
if (sibling != self) {
CGPoint translated = [self convertPoint:point toView:sibling] ;
UIView * other = [sibling hitTest:translated withEvent:event] ;
if (other) {
return other ;
}
}
}
}
return hit ;
}
So, I added a "marked set" to keep track of which view already had been visited, and it now all works :)
- (UIView *) hitTest: (CGPoint) point withEvent: (UIEvent *) event {
static NSMutableSet * markedViews = [NSMutableSet setWithCapacity:4] ;
UIView * hit = [super hitTest:point withEvent:event] ;
if (hit == nil) return nil ;
if (hit == self) {
for (UIView * sibling in hit.superview.subviews) {
if (sibling != hit) {
if ([markedViews containsObject:sibling]) {
continue ;
}
[markedViews addObject:sibling] ;
CGPoint translated = [hit convertPoint:point toView:sibling] ;
UIView * other = [sibling hitTest:translated withEvent:event] ;
[markedViews removeObject:sibling] ;
if (other) {
return other ;
}
}
}
}
return hit ;
}
Make a custom subclass for your view (that containts the other two subviews) and overwrite the hitTest:
method for it. In that method, check if the hitTest view is one of the two subviews, else return nil
. So all touches will be ignored on your surrounding view. Resulting in a touch on the view below, which can handle it itself.
//edit: (You get the hitTest view by calling UIView* view = [super hitTest:withEvent:];
.)
//edit2: i thought more of smth like that: :)
- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView * hit = [super hitTest:point withEvent:event] ;
if (hit == self) {
return nil;
}
return hit ;
}
;)