如何在被父视图部分裁剪的子视图上触发 UITapGestureRecognizer?
Posted
技术标签:
【中文标题】如何在被父视图部分裁剪的子视图上触发 UITapGestureRecognizer?【英文标题】:How to fire UITapGestureRecognizer on a child view that is partially clipped by parent view? 【发布时间】:2012-05-07 04:07:22 【问题描述】:设置:我有父视图 A。它有一个子视图 B。B 部分位于 A 的边界内,但部分位于 A 的边界之外(A 的 clipsToBounds=false)。我已将 UITapGestureRecognizer (UITGR) 附加到 B。
观察到:当我点击 B 在 A 范围内的部分时,UITGR 触发 OK。当我点击 B 在 A 范围之外的部分时,UITGR 不会触发。
预期/问题:当我点击 A 边界之外的 B 部分时,如何使 UITGR 触发?
【问题讨论】:
【参考方案1】:这句话将回答你关于为什么它的行为是这样的问题:
触摸事件。窗口对象使用命中测试和响应者链来找到接收触摸事件的视图。在命中测试中,窗口在视图层次结构的最顶层视图上调用 hitTest:withEvent:;此方法通过在视图层次结构中返回 YES 的每个视图上递归调用 pointInside:withEvent: 来继续,沿着层次结构向下进行,直到找到发生触摸的子视图。该视图成为命中测试视图。 (source)
一种解决方法是使用以下hitTest
定义创建您自己的UITapInSubviewsView
:
(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
NSEnumerator *reverseE = [self.subviews reverseObjectEnumerator];
UIView *iSubView;
while ((iSubView = [reverseE nextObject]))
UIView *viewWasHit = [iSubView hitTest:[self convertPoint:point toView:iSubView] withEvent:event];
if(viewWasHit)
return viewWasHit;
return [super hitTest:point withEvent:event];
然后你将这个类用于你的父视图。
(几周前我在 S.O. 的一篇文章中找到了这段代码,但我似乎再也找不到了;所以从我的项目中复制了它)。
【讨论】:
【参考方案2】:@sergio 对 Swift 4.2 的回答更新:
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
let reversedSubviews = subviews.reversed()
let hitSubview = reversedSubviews
.first(where: $0.hitTest(convert(point, to: $0), with: event) != nil )
if hitSubview != nil
return hitSubview
return super.hitTest(point, with: event)
【讨论】:
【参考方案3】:你必须为视图 A 编写自己的 hittest 函数。
也看这个问题:UITapGestureRecognizer not triggered in deep view hiearchy
【讨论】:
你能否扩展一下关于这个原因的答案——是因为默认的命中测试忽略了父视图边界之外的触摸吗?而且我需要测试自己的触摸是否在子范围内?以上是关于如何在被父视图部分裁剪的子视图上触发 UITapGestureRecognizer?的主要内容,如果未能解决你的问题,请参考以下文章
iOS - UIButton 作为 UIAlertController 上的子视图不触发事件
触发 UITapGesture 时从单元格的子视图中获取单元格的引用