UIView. Why Does A Subviews Outside its Parent's Extent Not Receive Touches?

后端 未结 5 1152
盖世英雄少女心
盖世英雄少女心 2020-12-04 19:29

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

相关标签:
5条回答
  • 2020-12-04 19:58

    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];
    }
    
    0 讨论(0)
  • 2020-12-04 20:06

    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
    
    0 讨论(0)
  • 2020-12-04 20:07

    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
    
    0 讨论(0)
  • 2020-12-04 20:12

    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

    0 讨论(0)
  • 2020-12-04 20:19
    • @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)
    }
    
    0 讨论(0)
提交回复
热议问题