在iOS上,如果一个父视图的userInteractionEnabled为NO,则所有子视图也会被禁用吗?

34
我认为当触摸或点击视图时,它的处理程序会首先被调用,然后再调用其父视图的处理程序(向上传播)。
但是,如果父视图的userInteractionEnabled属性设置为NO,那么所有子视图和后代也将禁用用户交互。如果我们只想禁用主视图,而不想禁用子视图,该怎么办呢?

啊哈,我在想是UIResponder的哪个规则或机制导致了这个问题...尽管现在我把它当作一个“事实”记住了。 - nonopolarity
4个回答

39

您可以覆盖hitTest(_:withEvent:)方法,忽略视图本身但仍将触摸传递给其子视图。

class ContainerStackView : UIStackView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let result = super.hitTest(point, with: event)
        if result == self { return nil }
        return result
    }
}

非常好。解决了很多问题。我用它来获取一个导航栏,可以通过触摸而不忽略按钮。我修改了这个代码:“return result is UIControl ? result : nil”。 - heiko
非常好的清晰解决方案。完美运作。 - Philipp Otto

29
如果有用的话,我在 Matt Neuburg 的《Programming iOS 5》第 467 页找到了这个内容:

userInteractionEnabled

如果设置为 NO,此视图(以及其子视图)将不接收触摸事件。在此视图或其子视图上的触摸事件将“穿透”到其后面的视图中。

此外,苹果的 iOS 事件处理指南也提到:

窗口对象使用命中测试和响应者链来查找要接收触摸事件的视图。在命中测试中,窗口在视图层次结构中的最顶层视图上调用 hitTest:withEvent: 方法;该方法通过递归地对每个返回 YES 的视图在其视图层次结构中调用 pointInside:withEvent: 方法进行处理,一直向下遍历视图层次结构,直到找到包含触摸点的子视图。这个视图成为命中测试视图。

并且 Matt Neuburg 的《Programming iOS 5》第 485 页提到,如果一个视图的 userInteractionEnabled 属性为 NO,或 hidden 属性为 YES,或者不透明度接近于 0,则该视图及其子视图将不会被 HitTest 遍历(因此不会考虑任何触摸事件)。
更新:如果我们在其他情况下考虑父子关系,它也可以按照这种方式工作。例如,在 HTML 中,如果有一个 div,并且所有的子元素都在这个 div 下面,现在这个 div 被设置为 display: none,那么所有的子元素都不会显示出来。因此,如果将父级设置为不与用户交互,那么子级不与用户交互也是有道理的。

1
对于那些正在实现自己的hitTest:withEvent:方法的人来说,这是一条卓越的信息。我不确定是否需要在父视图层面检查if(subview.userInteractionEnabled),但似乎默认的实现会检查自己的userInteractionEnabled属性。 - user244343
“fall through”指的是点击一个视图时,它后面的视图也会被响应。这是否意味着子视图上的按钮无法工作? - mfaani

17

你不能这样做,

相反,你可以像以下这样更改视图的排列:

Main View -> subViews

To

Container View -> Main View that you want to set as inactive
               -> other views that you want to still be active

所以你当前的主视图和子视图将变成兄弟视图,成为一个新容器视图的子视图。


4
“不能这样做”。“那”是什么?你指的是问题的哪一部分? - mfaani

3

第一种方法

- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
  if ([touch.view.superview isKindOfClass:[SuperViewParent class]]) return FALSE;
  return TRUE;
}

第二种方法

UITapGestureRecognizer *r = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(agentPickerTapped:)];
    r.cancelsTouchesInView = NO;
    [agentPicker addGestureRecognizer:r];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接