我在图层支持的视图上有一个子图层。子图层的内容设置为图像参考,并且是25x25矩形 当调用touchesBegan和touchesMoved方法时,我在超级层上执行命中测试。实际上,命中测试方法确实在触摸时返回子层,但仅在触摸图像的下半部分时才返回子层。如果触摸图像的上半部分,则返回超级图层。
我知道iPhone OS会补偿用户触摸的倾向低于预期。即使我将子图层调整为更大的尺寸(50x50),它也表现出相同的行为。
有什么想法?
答案 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
。