【问题标题】:UIView. Why Does A Subviews Outside its Parent's Extent Not Receive Touches?界面视图。为什么在其父范围之外的子视图没有收到触摸?
【发布时间】:2011-08-16 04:57:03
【问题描述】:

我有一个简单的 - 微不足道的 - UIView 父/子层次结构。一位家长(UIView)。一个孩子(UIButton)。父母的界限比它的孩子的界限小,因此孩子的一部分延伸到其父母的边界框之外。

问题是:父 bbox 之外的子部分接收到触摸。只有在父母的 bbox 内点击才允许子按钮接收触摸。

有人可以建议修复/解决方法吗?

更新

对于那些关注这个问题的人,这是我根据@Bastian 最优秀的答案实施的解决方案:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {

    BOOL isInside = [super pointInside:point withEvent:event];

    // identify the button view subclass
    UIButton *b = (UIButton *)[self viewWithTag:3232];
    CGPoint inButtonSpace = [self convertPoint:point toView:b];

    BOOL isInsideButton = [b pointInside:inButtonSpace withEvent:nil];

    if (isInsideButton) {

        return isInsideButton;

    } // if (YES == isInsideButton)

    return isInside;        
}

【问题讨论】:

  • 只是想指出您的hitTest:withEvent: 覆盖实际上并没有改变任何东西,因为它只是调用超级的实现。所以没必要。
  • @dugla:请从问题中删除您的解决方案并将其作为答案发布。
  • 在哪个子类中覆盖pointInside:,子视图?

标签: ios uiview hittest


【解决方案1】:

问题在于响应者链。当您触摸显示屏时,它会从父母向下传递给孩子。

所以..当你触摸屏幕时,父母会看到触摸超出了它自己的界限,所以孩子们甚至不会问。

执行此操作的函数是 hitTest。如果你有自己的 UIView 类,你可以覆盖它并自己返回按钮。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

【讨论】:

  • 我刚刚重新阅读了 UIView 文档并看到了这个。好的,所以要勾勒出这一点,在父母的 hitTest:withEvent: 内,我是否只需调用 [self pointInside:withEvent] ,然后当点位于孩子的 bbox 中但在父母的 bbox 之外时显式地调用孩子的 hitTest:withEvent ?
  • 是的...有这种可能..或者您可以直接返回父母hittest中的按钮对象
  • 仍然没有完全理解 hitTest:withEvent 与 pointInside:withEvent: 不同的意图。暂时把我的例子放在一边,hitTest:withEvent: 是否为视图边界的 bbox 之外的触摸返回 nil?如果是这样,那么我仍然需要 bbox 测试逻辑来识别在其父 bbox 之外的子部分内的命中。是吗?
  • 我为这两种相关方法都采用了一个覆盖的解决方案。
【解决方案2】:

根据Apple’s own documentation,我发现的最简单和最可靠的方法是在剪辑视图的超类中覆盖hitTest:withEvent:,如下所示:

- (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];
}

【讨论】:

    【解决方案3】:

    前提条件

    UIView(命名为容器)中有一个 UIButton(命名为 button1),并且 button1 部分位于容器边界之外。

    问题

    button1容器外的部分不会响应点击。

    解决方案

    从 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)
        }
    }
    

    别忘了给你的按钮1一个标签10086

    【讨论】:

      【解决方案4】:
      • @dugla,谢谢你的提问!
      • @Bastian 和 @laoyur,感谢您的回答!

      斯威夫特 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)
      }
      

      【讨论】:

        【解决方案5】:

        我手头也有同样的问题。您只需要覆盖:

        -(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event

        这是一个自定义 UIView 子类的工作代码,该子类仅为其越界子类创建可点击。

        @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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-02-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-31
          • 1970-01-01
          • 2014-10-07
          相关资源
          最近更新 更多