hitTest重叠CALayers

时间:2011-12-14 22:07:33

标签: iphone objective-c core-animation calayer

我有一个UIView,其中包含我使用CALayers作为子图层添加的图形。它是一个红色正方形,内部有一个蓝色三角形。我可以使用以下代码确定触摸了哪个形状:

CGPoint location = [gesture locationInView:self.view];
CALayer* layerThatWasTapped = [self.view.layer hitTest:location];
NSLog(@"Master Tap Location: %@", NSStringFromCGPoint(location));
NSLog(@"Tapped Layer Name: %@", layerThatWasTapped.name);
NSLog(@"Tapped Layer Parent: %@", layerThatWasTapped.superlayer.name);

int counter = layerThatWasTapped.superlayer.sublayers.count;
NSArray * subs =  layerThatWasTapped.superlayer.sublayers;

//Loop through all sublayers of the picture
for (int i=0; i<counter; i++) {
CALayer *layer = [subs objectAtIndex:i];
CAShapeLayer* loopLayer = (CAShapeLayer*)layerThatWasTapped.modelLayer;
CGPathRef loopPath = loopLayer.path;
CGPoint loopLoc = [gesture locationInView:cPage];        
loopLoc = [self.view.layer convertPoint:loopLoc toLayer:layer];
NSLog(@"loopLoc Tap Location: %@", NSStringFromCGPoint(loopLoc));

//determine if hit is on a layer
if (CGPathContainsPoint(loopPath, NULL, loopLoc, YES)) { 
NSLog(@"Layer %i Name: %@ Hit",i, layer.name);
} else {
NSLog(@"Layer %i Name: %@ No Hit",i, layer.name);
}
}

我的问题在于三角形边界与正方形重叠的区域。 这导致三角形记录命中,即使命中在命中之外 三角形路径。这是一个简化的例子(我可能在视图中堆叠了许多重叠的形状) 有没有办法循环遍历所有子层并对每个子层进行测试以查看它是否位于抽头点下方? 要么 有没有办法让我的图层的边界与它们的路径匹配,因此命中仅发生在可见区域?

1 个答案:

答案 0 :(得分:11)

由于您使用的是CAShapeLayer,因此非常简单。创建CAShapeLayer的子类并覆盖其containsPoint:方法,如下所示:

@implementation MyShapeLayer

- (BOOL)containsPoint:(CGPoint)p
{
    return CGPathContainsPoint(self.path, NULL, p, false);
}

@end

确保无论您在何处分配CAShapeLayer,都要将其更改为分配MyShapeLayer代替:

CAShapeLayer *triangle = [MyShapeLayer layer];  // this way
CAShapeLayer *triangle = [[MyShapeLayer alloc] init]; // or this way

最后,请记住,在调用-[CALayer hitTest:]时,您需要传入 superlayer 的坐标空间中的一个点:

CGPoint location = [gesture locationInView:self.view];
CALayer *myLayer = self.view.layer;
location = [myLayer.superlayer convertPoint:location fromLayer:myLayer];
CALayer* layerThatWasTapped = [myLayer hitTest:location];