72

我有一个自定义的 UIViewController,它的 UIView 占据了屏幕的一角,但是除了上面有一些按钮和东西的部分之外,它的大部分都是透明的。由于该视图上对象的布局,视图的框架可以覆盖其下方的一些按钮。如果他们没有触及任何重要的东西,我希望能够忽略对该视图的任何触摸,但我似乎只能传递实际的触摸事件(touchesEnded/nextResponder 的东西)。如果我有一个 UIButton 或类似的不使用 touchesEnded 的东西,我如何将触摸事件传递给它?

我不能只是手动找出要调用的按钮选择器,因为这个自定义 ViewController 可以用于许多不同的视图。我基本上需要一种方法来调用它:

[self.nextResponder touchesEnded:touches withEvent:event];

在 UIControl 类型上也是如此。

4

7 回答 7

136

可能最好的方法是hitTest:withEvent:在您想要忽略触摸的视图中覆盖。根据您的视图层次结构的复杂性,有几种简单的方法可以做到这一点。

如果您对要忽略的视图下方的视图有引用:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    // If the hitView is THIS view, return the view that you want to receive the touch instead:
    if (hitView == self) {
        return otherView;
    }
    // Else return the hitView (as it could be one of this view's buttons):
    return hitView;
}

如果您没有对视图的引用:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = [super hitTest:point withEvent:event];

    // If the hitView is THIS view, return nil and allow hitTest:withEvent: to
    // continue traversing the hierarchy to find the underlying view.
    if (hitView == self) {
        return nil;
    }
    // Else return the hitView (as it could be one of this view's buttons):
    return hitView;
}

我建议第一种方法是最强大的(如果可以获得对基础视图的引用)。

于 2011-10-10T23:42:23.137 回答
18

您还可以禁用用户交互,以便忽略触摸事件并将其传递给父级。

在斯威夫特:

yourView.isUserInteractionEnabled = false
于 2018-08-28T09:52:14.720 回答
17

快速回答:

你可以创建一个 UIView 子类,IgnoreTouchView。在情节提要中,将其设置在要通过触摸的 VC 视图上:

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

请注意,对于有问题的 UIView,您必须将 UserInteractionEnabled 设置为 true!

于 2017-01-02T23:25:50.803 回答
8

我还没有找到在对象之间传递 UITouch 事件的好方法。通常更好的方法是实现 hitTest 并返回 nil 如果该点不在您要处理的视图中:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
于 2011-10-10T23:37:46.553 回答
4

这是一个老问题,它出现在搜索的顶部。所以,我想我会发布另一个可能更适合您的情况的解决方案。在我的例子中,我想将一个标签拖到另一个标签上,并且我想在 hitTest 中获得下面的标签。最简单的做法是将其设置userInteractionEnabledNOor false,然后执行 hitTest,如果需要再次移动它,再将其设置回去。这是 Swift 中的代码示例:

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first
    let location = touch?.locationInView(self.view)
    self.label1.userInteractionEnabled = false
    let view = self.view.hitTest(location!, withEvent: nil) as? UILabel
    print(view?.text)
    self.label1.userInteractionEnabled = true
}
于 2016-02-28T03:00:00.933 回答
3

我的问题很相似:我有一个带有复杂触摸响应器的 UIControl,但它在嵌入滚动视图时表现得很有趣......

当子视图具有活动的触摸手势时,这可以防止父滚动(或可以修改以防止其他交互)。

我的解决方案:

在子视图中为 didBeginInteraction 和 didEndInteraction 创建一个委托协议。

从 touchesBegan 调用 delegate.didBeginInteraction

从 touchesEnded 和 touchesCancelled 调用 delegate.didEndInteraction

在父控制器中,实现didBegin和didEnd协议,设置delegate

然后在didBegin 中设置scrollView.scrollEnabled = NO,在didEnd 中设置scrollEnabled = YES。

希望这对用例与提出的问题略有不同的人有所帮助!

于 2017-02-14T21:40:45.220 回答
1

对我来说,简单的解决方案是isUserInteractionEnabled在添加到按钮的特定子视图上设置为 false。

// Some example view components
let button: UIButton = ...
let stackView: UIStackView = ...

// Setting this to false will make all touches ignored by this subview
stackView.isUserInteractionEnabled = false

// Maybe this was my specific case, but I had a pretty custom button with a bunch of subviews.
button.addSubview(stackView)
于 2019-11-26T13:58:39.350 回答