我在基于层的视图上有一个子层。子层的内容设置为“图像参考”,并且为25x25矩形。当调用touchesBegan和touchesMoved方法时,我会在超级层上执行命中测试。实际上,点击测试方法的确会返回子图层,但只有在触摸了图像的下半部分时,才返回子图层。如果触摸了图像的上半部分,它将返回上一层。
我确实知道iPhone OS补偿了用户触摸的趋势低于预期的趋势。即使我将子图层的大小调整为更大的尺寸(50x50),它也表现出相同的行为。
有什么想法吗?
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];
(为完整性起见,请重复其他帖子的答案)
似乎该图层不仅在底部像素上接受触摸。相反,似乎屏幕层上的“实际”和该层的内容是由不同的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
接受的答案不能解决问题,但是schwa的答案可以解决。
我有一个窗口,一个视图控制器和一个视图。该视图接收触摸并在其层上调用hitTest:
。我遇到了同样的问题,发现视图中的点是正确的,但是在hitTest:
中,该点的y坐标偏离了20px,这恰好是状态栏的高度。
如schwa所述,通过将点映射到上层的坐标系,此问题已解决。 “神秘”超层是超级视图的根层。就我而言,它是窗口层(帧(0,0,320,480))。
如果仅在下部识别出触摸,则一种可能性是另一个子视图覆盖了该子视图的上半部分。您有多个子视图还是只有一个图像?如果您有多个子视图,并且每个子视图的背景颜色均为clearColor,请尝试为其提供纯色背景色以进行测试。这样,您将知道子视图是否被另一个子视图覆盖。
希望有所帮助。
我简化了代码以查找问题。我在屏幕上只有一个子图层,没有子视图。上面列出的代码是我在模拟器中运行的代码。由于屏幕上只有2层,即主机视图的支持层和我添加的子层,因此没有任何东西会影响点击测试。
如果不清楚,则在触摸子层的顶部时,命中测试将改为返回衬里层。另外,如果我在子层正下方的某个点触摸衬里层,则命中测试会返回该子层。
很难解释,但是如果您运行代码,它将变得很清楚。
谢谢
感谢凯文...
我最终只是使用UIViews重做代码。仅试图弄清楚为什么UIView不知道触摸了哪一层是一个很大的麻烦。我支持该想法的全部理由是,我一次可以在屏幕上显示100多个项目,并且认为Layers在处理器上比UIViews更容易。我已经启动并运行了该应用程序,但100次浏览并没有给它带来任何麻烦。
我对其他人的建议是尽可能坚持对UIViews进行命中测试,这使代码更加简单]]
///添加frame.origin.y
当我尝试您的代码时,我得到几乎正确的行为,除了在子层的最边缘通常不识别触摸。我对所发生的事情感到困惑。您可能想尝试做一些事情,例如在注册触摸时,在发生触摸的地方向上铺一层新的微小层(例如5x5正方形的某种颜色),然后在3秒后再次将其删除。这样,您可以跟踪CA认为发生触摸的位置与光标实际在哪里。