ios hitTest返回错误的UIView

wgxvkvu9  于 2023-06-25  发布在  iOS
关注(0)|答案(3)|浏览(118)

我有一个视图层次结构,其中包含滚动视图上的较小视图。每个视图中可以有子视图,如按钮等。
由于某些原因,视图上的按钮没有被单击;探索这一点进一步表明,当滚动视图接收touchBegan事件时,按钮没有。调用hitTest:事件:消息显示按钮未返回,即使它在限制范围内。
我包含了一个日志输出,描述了触摸在滚动视图中的位置,从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使用 * CGZero * 初始化,当创建 UIImageView 时,它的框架被设置为图像的大小+图像底部标签的额外空间。
更新2:当用相同的位置调用 [self.layer hitTest:location] 时,我得到了 * 正确 * 视图的图层!这是怎么回事

drkbr07n

drkbr07n1#

hitTest:withEvent:从窗口开始。每个视图在测试自己之前先测试它的子视图,依此类推。但是,如果视图的userInteractionEnabled为NO,它将从hitTest:withEvent:返回nil,而不测试它的子视图。这样的视图当然是经过点击测试的,但它立即回复说,无论是它还是它的任何子视图都不是点击视图。
您的UIScrollView的userInteractinonEnabled设置为NO。因此,当VCViewContainersView测试其子视图UIScrollView时,UIScrollView返回nil,因为它的userInteractionEnabled为NO,VCViewContainersView在自身上使用pointInside:withEvent:,发现触摸在自身内,并返回自身作为命中视图(搜索结束)。这解释了你得到的结果。
当你通过层进行点击测试时,这不会发生的原因是层是不可触摸的,并且对触摸规则一无所知,所以它们忽略了userInteractionEnabled,这是一个视图特征,而不是一个层特征。图层点击测试是一种繁琐的工作,仅适用于视图包含整个图层层次结构(没有视图层次结构)并且您想模拟特定图层是可触摸的情况。文档确实告诉您,层的hitTest:与视图的hitTest:withEvent:的逻辑不同,尽管它们没有确切解释如何。
我不知道你为什么要把滚动视图设置为不可触摸的,但是如果这对你很重要,你可以在UIScrollView子类中覆盖hitTest:withEvent:,这样它就测试它的子视图,但是如果所有子视图都返回nil,它就返回nil。

tzxcd3kk

tzxcd3kk2#

您可能想尝试对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

应该可以了

thigvfpy

thigvfpy3#

如果我正确理解视图堆栈,那么你的按钮是一些UIImageView的子视图-它的userInteractionEnabled属性设置为NO(默认情况下)-所以图像视图和它的所有子视图不会接收任何触摸事件。
将image view的userInteractionEnabled属性设置为YES可以解决此问题

相关问题