如何忽略触摸事件并将它们传递给另一个子视图的 UIControl 对象?
Posted
技术标签:
【中文标题】如何忽略触摸事件并将它们传递给另一个子视图的 UIControl 对象?【英文标题】:How to ignore touch events and pass them to another subview's UIControl objects? 【发布时间】:2011-10-10 22:30:25 【问题描述】:我有一个自定义的 UIViewController,它的 UIView 占据了屏幕的一角,但是除了上面有一些按钮和东西的部分之外,它大部分都是透明的。由于该视图上对象的布局,视图的框架可以覆盖其下方的一些按钮。如果他们没有触及任何重要的东西,我希望能够忽略对该视图的任何触摸,但我似乎只能传递实际的触摸事件(touchesEnded/nextResponder 的东西)。如果我有一个 UIButton 或类似的不使用 touchesEnded 的东西,我如何将触摸事件传递给它?
我不能只是手动找出要调用的按钮选择器,因为这个自定义 ViewController 可以用于许多不同的视图。我基本上需要一种方法来调用它:
[self.nextResponder touchesEnded:touches withEvent:event];
也适用于 UIControl 类型。
【问题讨论】:
【参考方案1】:可能最好的方法是在您希望忽略触摸的视图中覆盖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;
我会推荐第一种方法,因为它是最健壮的(如果可以获得对基础视图的引用)。
【讨论】:
这似乎是我想要做的,但我想忽略触摸的视图是我创建的 UIViewController 的视图属性,那么有没有办法覆盖该视图的 hitTest 函数?跨度> @chris:当然,只需将其子类化并放入如上所示的覆盖方法即可。这意味着您将拥有一个额外的.h
和.m
文件,其中几乎没有代码,但它会完成这项工作。此外,直通视图是工具包中的一个有用的子类。如果您的视图控制器在nib
中创建视图,只需进入 IB 文件并将视图的类更改为身份检查器中的新子类。如果它是通过编程方式创建的,则在 loadView
中创建并分配您的视图子类(参见编辑)。
好建议!我整天都在寻找一个好的解决方案。谢谢。
效果很好,但是我希望仅当事件是点击事件而不是拖动事件时才返回 otherView。有没有办法区分不同类型的事件?
@htafoya:听起来你需要手势识别器。特定手势(例如点击)的手势识别器仅由它们所附加的视图处理。一般来说,您应该使用触摸处理 (hitTest:withEvent:
) 来决定视图是否应该接收触摸,但使用手势识别器来区分不同类型的触摸。【参考方案2】:
您还可以禁用用户交互,以便忽略触摸事件并将其传递给父级。
在 Swift 中:
yourView.isUserInteractionEnabled = false
【讨论】:
我认为这不对。如果 isUserInteractionEnabled 为真,则忽略事件并(根据 Apple 文档)“从事件队列中删除”。即他们不会像 OP 想要的那样被传递到下一个视图。【参考方案3】:快速回答:
您可以创建一个 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!
【讨论】:
Kevin,我将一些字符编辑到了最新的 Swift 版本,2017 年 4 月。 (随意更改等)干杯【参考方案4】:我还没有找到在对象之间传递 UITouch 事件的好方法。通常更好的方法是实现 hitTest 并在该点不在您要处理的视图中时返回 nil:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
【讨论】:
【参考方案5】:这是一个老问题,它出现在搜索的顶部。所以,我想我会发布另一个可能更适合您的情况的解决方案。在我的情况下,我想将一个标签拖到另一个标签上,并且我想在 hitTest 中获取下面的标签。最简单的做法是将userInteractionEnabled
设置为NO
或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
【讨论】:
感谢您的解决方案。 这行不通,因为一旦你将 label1.userInteractionEnabled 设置为 false,那么 label1 以后甚至不会检测到触摸。 @FredA。这就是为什么我写道:“......如果你需要再次移动它,然后将它重新设置回来”。也许我不够清楚? 是的,您可以重新设置它,但不能在 touchesEnded 内部。您可以在其他地方设置它,但不能在 touch 方法中设置。 @FredA。有用。我不确定问题是什么。触摸结束。禁用用户交互。做最热门的。将交互设置回启用。这是一种有据可查的方法。你一定是在做一些不同的事情。【参考方案6】:我的问题很相似:我有一个带有复杂触摸响应器的 UIControl,但是当它嵌入到滚动视图中时表现得很有趣......
当子视图有一个活动的触摸手势时,这可以防止父滚动(或可以修改以防止其他交互)。
我的解决方案:
在子视图中为 didBeginInteraction 和 didEndInteraction 创建一个委托协议。
从 touchesBegan 调用 delegate.didBeginInteraction
从 touchesEnded 和 touchesCancelled 调用 delegate.didEndInteraction
在父控制器中,实现didBegin和didEnd协议,设置委托
然后在 didBegin 中设置 scrollView.scrollEnabled = NO,在 didEnd 中设置 scrollEnabled = YES。
希望这对用例与提出的问题略有不同的人有所帮助!
【讨论】:
哇,我认为我找不到解决我在自定义 UIControl 中遇到的确切利基问题的方法。谢谢大佬!【参考方案7】:对我来说,简单的解决方案是在添加到按钮的特定子视图上将 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)
【讨论】:
以上是关于如何忽略触摸事件并将它们传递给另一个子视图的 UIControl 对象?的主要内容,如果未能解决你的问题,请参考以下文章
如何将数据从子视图传递到父视图到 SwiftUI 中的另一个子视图?