仅在触及层的下半部分时才返回层击测试

问题描述 投票:4回答:8

我在基于层的视图上有一个子层。子层的内容设置为“图像参考”,并且为25x25矩形。当调用touchesBegan和touchesMoved方法时,我会在超级层上执行命中测试。实际上,点击测试方法的确会返回子图层,但只有在触摸了图像的下半部分时,才返回子图层。如果触摸了图像的上半部分,它将返回上一层。

我确实知道iPhone OS补偿了用户触摸的趋势低于预期的趋势。即使我将子图层的大小调整为更大的尺寸(50x50),它也表现出相同的行为。

有什么想法吗?

iphone cocoa-touch core-animation
8个回答
11
投票

hitTest的文档说:

/* Returns the farthest descendant of the layer containing point 'p'.
 * Siblings are searched in top-to-bottom order. 'p' is in the
 * coordinate system of the receiver's superlayer. */

所以您需要做(这样的事情:):

CGPoint thePoint = [touch locationInView:self];
thePoint = [self.layer convertPoint:thePoint toLayer:self.layer.superlayer];
CALayer *theLayer = [self.layer hitTest:thePoint];

(为完整性起见,请重复其他帖子的答案)


1
投票

似乎该图层不仅在底部像素上接受触摸。相反,似乎屏幕层上的“实际”和该层的内容是由不同的CGRects定义的。图像显示在预期的坐标上,而响应触摸的图层在图像下方偏移。在下面,我的意思是图像的原点在(200,200),而响应触摸的图层的原点在(200,220)。

下面,我发布了一些我用来重现问题的测试代码。首先是我的视图子类,然后是我的视图控制器。非常感谢此问题背后的任何推理。

我的视图子类:

#import "myView.h"
#import <QuartzCore/QuartzCore.h>


@implementation myView


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    CGPoint currentLocation = [[touches anyObject] locationInView:self];

    CALayer *currentLayer = [self.layer hitTest:currentLocation];

    CGPoint center = [currentLayer position];

    NSLog([currentLayer valueForKey:@"isRootLayer"]);

    if(![currentLayer valueForKey:@"isRootLayer"]) {

        if (center.x != 200) {

            [currentLayer setPosition:CGPointMake(200.0f, 200.0f)];     

        } else {
            [currentLayer setPosition:CGPointMake(100.0f, 100.0f)];     

        }
    }
}


- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        // Initialization code
    }
    return self;
}


- (void)drawRect:(CGRect)rect {
    // Drawing code
}


- (void)dealloc {
    [super dealloc];
}


@end

我的视图控制器:

#import "layerTestViewController.h"
#import <QuartzCore/QuartzCore.h>


#define ELEMENT_PERIOD_SIZE 50
#define ELEMENT_GROUP_SIZE 50

@implementation layerTestViewController





// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];

    CALayer  *myLayer = [CALayer layer];
    CGRect layerFrame =CGRectMake(0.0f, 0.0f, ELEMENT_GROUP_SIZE, ELEMENT_PERIOD_SIZE);
    myLayer.frame = layerFrame; 

    [myLayer setName:[NSString stringWithString:@"test"]];
    [myLayer setValue:[NSString stringWithString:@"testkey"] forKey:@"key"];

    UIGraphicsBeginImageContext(layerFrame.size);
    [[UIColor blueColor] set];
    UIRectFill(layerFrame);
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    myLayer.contents = (id)[theImage CGImage];
    myLayer.position = CGPointMake(100.0f, 100.0f);

    [self.view.layer addSublayer:myLayer];   

    [self.view.layer setValue:[NSString stringWithString:@"YES"] forKey:@"isRootLayer"];
    NSLog([self.view.layer valueForKey:@"isRootLayer"]);

}




- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
    // Release anything that's not essential, such as cached data
}


- (void)dealloc {
    [super dealloc];
}

@end

1
投票

接受的答案不能解决问题,但是schwa的答案可以解决。

我有一个窗口,一个视图控制器和一个视图。该视图接收触摸并在其层上调用hitTest:。我遇到了同样的问题,发现视图中的点是正确的,但是在hitTest:中,该点的y坐标偏离了20px,这恰好是状态栏的高度。

如schwa所述,通过将点映射到上层的坐标系,此问题已解决。 “神秘”超层是超级视图的根层。就我而言,它是窗口层(帧(0,0,320,480))。


0
投票

如果仅在下部识别出触摸,则一种可能性是另一个子视图覆盖了该子视图的上半部分。您有多个子视图还是只有一个图像?如果您有多个子视图,并且每个子视图的背景颜色均为clearColor,请尝试为其提供纯色背景色以进行测试。这样,您将知道子视图是否被另一个子视图覆盖。

希望有所帮助。


0
投票

我简化了代码以查找问题。我在屏幕上只有一个子图层,没有子视图。上面列出的代码是我在模拟器中运行的代码。由于屏幕上只有2层,即主机视图的支持层和我添加的子层,因此没有任何东西会影响点击测试。

如果不清楚,则在触摸子层的顶部时,命中测试将改为返回衬里层。另外,如果我在子层正下方的某个点触摸衬里层,则命中测试会返回该子层。

很难解释,但是如果您运行代码,它将变得很清楚。

谢谢


0
投票

感谢凯文...

我最终只是使用UIViews重做代码。仅试图弄清楚为什么UIView不知道触摸了哪一层是一个很大的麻烦。我支持该想法的全部理由是,我一次可以在屏幕上显示100多个项目,并且认为Layers在处理器上比UIViews更容易。我已经启动并运行了该应用程序,但100次浏览并没有给它带来任何麻烦。

我对其他人的建议是尽可能坚持对UIViews进行命中测试,这使代码更加简单]]


0
投票

///添加frame.origin.y


-1
投票

当我尝试您的代码时,我得到几乎正确的行为,除了在子层的最边缘通常不识别触摸。我对所发生的事情感到困惑。您可能想尝试做一些事情,例如在注册触摸时,在发生触摸的地方向上铺一层新的微小层(例如5x5正方形的某种颜色),然后在3秒后再次将其删除。这样,您可以跟踪CA认为发生触摸的位置与光标实际在哪里。

© www.soinside.com 2019 - 2024. All rights reserved.