【问题标题】:hitTest returns wrong UIViewhitTest 返回错误的 UIView
【发布时间】:2010-10-22 08:39:00
【问题描述】:

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

由于某种原因,视图上的按钮没有被点击;进一步探索表明,当滚动视图接收到 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]

更新:除了 UIWindowVCViewsContainerView,所有视图都是使用 initWithFrame: 或在按钮的情况,buttonWithType:。 VCViewContainer 使用 CGRectZero 进行初始化,当创建 UIImageView 时,其框架设置为图像大小 + 底部标签的额外空间。

更新 2:当使用相同的位置调用 [self.layer hitTest:location] 时,我得到了 正确 视图的层!这是怎么回事……?

【问题讨论】:

  • UIRoundedRectButton 按钮。
  • 我们需要看看您是如何创建视图层次结构的...InterfaceBuilder、代码等...
  • @KennyTM:如果不清楚,它会说“在(实际触摸的项目)上触摸(位置)”。请注意,触摸的项目是 VCViewContainerView 而不是 UIRoundedRectButton。
  • @jv42:我已经添加了解释;添加代码是一种耻辱,因为它分散在所有地方。如果你仍然需要代码,我会破解一些东西。

标签: iphone ipad ios hittest


【解决方案1】:

hitTest:withEvent: 从窗口开始。每个视图在测试自身之前测试其子视图,以此类推,递归。但是,如果视图的 userInteractionEnabled 为 NO,它会从 hitTest:withEvent: 返回 nil,而不测试其子视图。这样的视图当然是经过命中测试的,但它会立即回复说它和它的任何子视图都不是命中视图。

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

通过图层进行命中测试时不会发生这种情况的原因是图层不可触摸并且对触摸规则一无所知,因此它们忽略了 userInteractionEnabled,这是一个视图功能,而不是层特征。 Layer hit-testing 是一种杂物,仅适用于当视图包含整个层层次结构(没有视图层次结构)并且您想要模拟特定层是可触摸的时。文档确实告诉您,层的 hitTest: 的逻辑与视图的 hitTest:withEvent: 的逻辑不同,尽管他们未能准确解释如何。

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

【讨论】:

  • +1 感谢hitTest:withEvent:userInteractionEnabledNO 时返回nil 的解释
  • @AlexanderFarber 对于触摸传递和命中测试的完整逆向工程,请参见此处:apeth.com/iOSBook/ch18.html#_hit_testing
  • 谢谢你,Matt,我在 Safari 上读过你的书——它非常有用。在stackoverflow.com/questions/22582649/…,您对我当前的问题(赏金 500)有什么建议吗?我无法确定正确的hitTest:withEvent: 那里。
  • 我认为您在这方面得到了很多帮助!
【解决方案2】:

您可能想尝试子类化您的 UIScrollView 以接收对其中对象的触摸。

默认情况下,您的触摸将由您的 UIScrollView 接收,因此如果您想接收对其内部对象的触摸,则需要明确设置。

这是一个可以做到这一点的工作子类。感谢 SO 社区,因为我相信我最初在这里找到了类似的东西,只是现在无法挖掘原始帖子。

这是.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface TouchScroller : UIScrollView 
{
}

@end

还有.m:

#import "TouchScroller.h"


@implementation TouchScroller

- (id)initWithFrame:(CGRect)frame 
{
return [super initWithFrame:frame];
}

- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event 
{   
// If not dragging, send event to next responder
if (!self.dragging) 
    [self.nextResponder touchesEnded: touches withEvent:event]; 
else
    [super touchesEnded: touches withEvent: event];
}

@end

应该这样做。

【讨论】:

    【解决方案3】:

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

    设置image view的userInteractionEnabled属性为YES一定能解决问题

    【讨论】:

    • 以防万一我尝试过 - 但不幸的是事实并非如此。我不认为你说的是​​真的;文档指定事件处理程序的搜索是从上到下的,因此应首先测试按钮。无论如何,hitTest: 根本不受用户交互的影响..
    猜你喜欢
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    相关资源
    最近更新 更多