hitTest返回错误的UIView

Avi*_*Dov 5 iphone hittest ipad ios

我有一个视图层次结构,包含滚动视图上较小的视图.每个视图都可以包含子视图,例如按钮等.

由于某种原因,视图上的按钮没有被点击; 探索这一点进一步表明,虽然滚动视图接收到touchBegan事件,但按钮却没有.调用hitTest:event:消息显示该按钮未返回,即使它在限制范围内.

我已经包含了一个日志输出,描述了滚动视图中触摸的位置,从hitTest返回的项目,触摸位置,如果我调用locationInView:使用预期项目,以及预期项目的层次结构(打印了框架).从这个输出我可以推断该按钮应该被调用...

有谁能解释一下?我错过了什么吗?

touched ({451, 309}) on <VCViewContainersView: 0x4b31ee0; frame = (0 0; 748 1024); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x4b32130>> (location in expected item: {17, 7.5})
expected touched item is:
view: <UIButtonLabel: 0x482b920; frame = (32 5; 36 19); text = 'Click'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x4831370>>, layer transform: [1, 0, 0, 1, 0, 0]
 view: <UIRoundedRectButton: 0x482c100; frame = (50 50; 100 30); opaque = NO; layer = <CALayer: 0x482c450>>, layer transform: [1, 0, 0, 1, 0, 0]
  view: <UIImageView: 0x480f290; frame = (0 0; 320 255); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x480e840>>, layer transform: [1, 0, 0, 1, 0, 0]
   view: <VCViewContainer: 0x4b333c0; frame = (352 246.5; 320 471.75); layer = <CALayer: 0x4b33d50>>, layer transform: [1, 0, 0, 1, 0, 0]
    view: <UIScrollView: 0x4b32600; frame = (0 0; 1024 748); clipsToBounds = YES; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x4b32780>>, layer transform: [1, 0, 0, 1, 0, 0]
     view: <VCViewsContainerView: 0x4b31ee0; frame = (0 0; 748 1024); transform = [0, 1, -1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x4b32130>>, layer transform: [0, 1, -1, 0, 0, 0]
      view: <UIWindow: 0x4b1d590; frame = (0 0; 768 1024); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x4b1d6d0>>, layer transform: [1, 0, 0, 1, 0, 0]
Run Code Online (Sandbox Code Playgroud)

更新:除了UIWindowVCViewsContainerView之外,所有视图都是使用initWithFrame以编程方式创建的:或者在按钮的情况下,buttonWithType : . 使用CGRectZero初始化VCViewContainer,并在创建UIImageView时,将其框架设置为图像的大小+底部标签的附加空间.

更新2:当使用相同的位置调用[self.layer hitTest:location]时,我得到了正确视图的图层!这里发生了什么...?

mat*_*att 5

hitTest:withEvent:从窗口开始.每个视图在测试自身之前测试其子视图,依此类推,递归.但是,如果视图的userInteractionEnabled为NO,则它从hitTest:withEvent:返回nil,而不测试其子视图.这样的观点肯定经过了热门测试,但它立即回复说它既没有也没有任何子视图都是热门视图.

您UIScrollView将其userInteractinonEnabled设置为NO.因此,当VCViewContainersView测试其子视图UIScrollView,并且UIScrollView返回nil因为其userInteractionEnabled为NO,VCViewContainersView使用pointInside:withEvent:on本身,发现触摸在其自身内,并将其自身返回为命中视图(并且搜索结束).这解释了您获得的结果.

当你通过图层进行命中测试时没有发生这种情况的原因是图层不可触摸,对触摸规则一无所知,因此他们忽略了userInteractionEnabled,这是一个视图特征,而不是图层特征.图层命中测试是一种kludge,仅适用于视图包含整个图层层次结构(没有视图层次结构)并且您希望模拟特定图层可触摸的情况.文档确实告诉你,图层的hitTest的逻辑是不同的,而不是视图的hitTest:withEvent:,但它们无法准确解释如何.

我不知道为什么你将滚动视图设置为不可触摸,但如果这对你很重要,你可以在UIScrollView子类中覆盖hitTest:withEvent:以便它测试它的子视图但是如果它们全部返回则返回nil零.

  • @AlexanderFarber关于触摸传输和命中测试的全面逆向工程,请参见:http://www.apeth.com/iOSBook/ch18.html#_hit_testing (2认同)

Vla*_*mir 0

如果我正确理解视图堆栈,那么您的按钮是某些 UIImageView 的子视图 - 它的userInteractionEnabled属性设置为 NO (默认情况下) - 因此图像视图及其所有子视图不会接收任何触摸事件。

设置图像视图的userInteractionEnabled属性YES必须解决问题