2010-10-22 48 views
5

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

由于某种原因,视图上的按钮未被点击;探索这进一步表明,虽然滚动视图收到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:位置]以相同的位置,我得到的正确视图层!这里发生了什么...?

+0

UIRoundedRectButton *是*按钮。 – kennytm 2010-10-22 08:42:27

+0

我们需要看看你是如何创建你的视图层次结构... InterfaceBuilder,代码等... – jv42 2010-10-22 08:47:51

+0

@KennyTM:如果它不清楚,它说“触摸(位置)(实际上触摸的项​​目)”。请注意,触摸的项目是VCViewContainerView和_not_ UIRoundedRectButton。 – 2010-10-22 08:49:12

回答

0

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

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

+0

以防万一我尝试过 - 但不幸的是,情况并非如此。我不认为你说的是​​真的;文档指定对事件处理程序的搜索是从上到下的搜索,因此应该先测试按钮。在任何情况下,hitTest:都不会受到用户交互的影响.. – 2010-10-22 10:45:40

5

则hitTest:withEvent:方法开始在窗口。每个视图在测试自己之前测试它的子视图,等等,递归。但是,如果视图的userInteractionEnabled为NO,它将从hitTest:withEvent:返回nil,而不测试其子视图。这样的观点当然受到了严格的考验,但它立即回应说,它的任何子视图都不是热门视图。

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

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

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

+0

+1感谢解释,hitTest:withEvent:在'userInteractionEnabled'为'NO'时返回'nil' – 2014-03-24 13:48:42

+2

@AlexanderFarber对于我的完全反向触摸传输和命中测试的工程,请看这里:http://www.apeth.com/iOSBook/ch18.html#_hit_testing – matt 2014-03-24 16:25:37

+0

谢谢马特,我在Safari阅读你的书 - 这是非常有用的。你碰巧有任何建议,我目前的问题(赏金500)在http://stackoverflow.com/questions/22582649/dragging-views-on-a-scroll-view-touchesbegan-is-received-but-no- touchesended-o?我无法弄清楚正确的'hitTest:withEvent:'在那里。 – 2014-03-24 19:54:27

1

您可能想要尝试继承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 

应该这样做。