I have a simple - trivial - UIView parent/child hierarchy. One parent (UIView). One child (UIButton). The parents bounds are smaller then it\'s child\'s bounds so that a por
Per Apple’s own documentation, the simplest and most reliable way I have found to do this is to override hitTest:withEvent:
in the superclass of your clipped view to look like the following:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
// Convert the point to the target view's coordinate system.
// The target view isn't necessarily the immediate subview
CGPoint pointForTargetView = [self.targetView convertPoint:point fromView:self];
if (CGRectContainsPoint(self.targetView.bounds, pointForTargetView)) {
// The target view may have its view hierarchy,
// so call its hitTest method to return the right hit-test view
return [self.targetView hitTest:pointForTargetView withEvent:event];
}
return [super hitTest:point withEvent:event];
}
I had the same exact problem at hand. You only need to override:
-(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event
Here is working code for a custom UIView subclass created solely to be tappable for its out-of-bounds children.
@implementation ARQViewToRightmost
// We should not need to override this one !!!
/*-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self isInside:point])
return self;
return nil;
}*/
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if ([self isInside:point])
return YES; // That answer will make hitTest continue recursively probing the view's subviews for hit
return NO;
}
// Check to see if the point is within the bounds of the view. We've extended the bounds to the max right
// Make sure you do not have any other sibling UIViews to the right of this one as they will never receive events
- (BOOL) isInside:(CGPoint)point
{
CGFloat maxWidth = CGFLOAT_MAX;
CGRect rect = CGRectMake(0, 0, maxWidth, self.frame.size.height);
if (CGRectContainsPoint(rect, point))
return YES;
return NO;
}
@end
The problem is the responder chain. When you touch the display it will go down from the parents to the childen.
So .. when you touch the screen the parent will see that the touch is outside of it's own bounds and so the children will not even asked.
The function that does that is the hitTest. If you have your own UIView class you can overwrite it and return the button by yourself.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
Precondition:
You have a UIButton
(named as button1) inside a UIView
(named as container), and button1 is partially outside the container's bounds.
Problem:
the part outside the container of button1 will not response click.
Solution:
subclass your container from UIView:
class Container: UIView {
override func pointInside(point: CGPoint, withEvent event: UIEvent?) -> Bool {
let closeButton = viewWithTag(10086) as! UIButton //<-- note the tag value
if closeButton.pointInside(convertPoint(point, toView: closeButton), withEvent: event) {
return true
}
return super.pointInside(point, withEvent: event)
}
}
Don't forget to give your button1 a tag of 10086
- @dugla, thank you for the question!
- @Bastian and @laoyur, thanks to you for answers!
Swift 4
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
if yourChildView.point(inside: convert(point, to: yourChildView), with: event) {
return true
}
return super.point(inside: point, with: event)
}