当触摸下半部分时,图层命中测试仅返回图层

时间:2008-12-30 18:25:05

标签: iphone cocoa-touch core-animation

我在图层支持的视图上有一个子图层。子图层的内容设置为图像参考,并且是25x25矩形 当调用touchesBegan和touchesMoved方法时,我在超级层上执行命中测试。实际上,命中测试方法确实在触摸时返回子层,但仅在触摸图像的下半部分时才返回子层。如果触摸图像的上半部分,则返回超级图层。

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

有什么想法?

8 个答案:

答案 0 :(得分: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 :(得分:1)

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

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

My View子类:

#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

答案 2 :(得分:1)

接受的答案并没有解决问题,但schwa的答案确实如此。

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

通过schwa建议将点映射到超层的坐标系,这个问题是固定的。 &#34;神秘&#34; superlayer是superview的根层。在我的例子中,它是窗口层(帧(0,0,320,480))。

答案 3 :(得分:0)

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

希望有所帮助。

答案 4 :(得分:0)

我已经简化了我的代码以找到问题。我在屏幕上只有一个子图层,没有子视图。上面列出的代码就是我在模拟器中运行的代码。由于屏幕上只有2层,主机视图的后备层和我添加的子层,因此没有任何内容会干扰命中测试。

如果不清楚,当触摸子层的顶部时,命中测试返回背衬层。此外,如果我在子层正下方的某个点触摸背衬层,则通过命中测试返回子图层。

很难解释,但如果你运行代码,它就会变得清晰。

由于

答案 5 :(得分:0)

谢谢凯文......

我最终只是用UIViews重做我的代码。只是想弄清楚为什么UIView不知道触摸了什么层,这太麻烦了。这个想法背后的全部理由是我一次会在屏幕上有100多个项目,并且认为Layers在处理器上比UIViews更容易。我现在已经启动了应用程序并且100个视图没有给它任何打嗝。

我对其他人的建议只是尽可能坚持使用热门测试UIViews,它使代码更简单

答案 6 :(得分:0)

///添加frame.origin.y

    public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let convertPoint = CGPoint(x:point.x, y:(point.y + frame.origin.y))
        let superPiont = self.layer.convert(convertPoint, to:layer.superlayer)
        selectlayer = layer.superlayer?.hitTest(superPiont)
        }

答案 7 :(得分:-1)

当我尝试你的代码时,我得到几乎正确的行为,除了触摸通常在子层的边缘无法识别。对于发生的事情,我有点困惑。你可能想尝试做一些事情,比如当你注册触摸时,在触摸发生的地方抛出一个新的小层(比如5x5平方的某种颜色),然后在3秒后再将它移除。这样,您就可以跟踪CA认为触摸发生的位置与光标实际位置的对比。

顺便说一句,您不需要为蓝色内容创建CGImage,只需将图层的backgroundColor设置为[UIColor blueColor].CGColor