1
votes

I can't understand why a CAShapeLayer does not respond to hitTest

This function always goes to // touches is outside

How do I detect a touch on a CAShapeLayer?

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

    currentPoint = [[touches anyObject] locationInView:self];

    for (CAShapeLayer *layer in self.layer.sublayers) {    

        if(layer == shapeLayer) {

            if([layer hitTest:currentPoint])
            {
                // touche is on the layer
            }
            else {
                // touche is outside
            }

        }

    }       

}
2

2 Answers

6
votes

After banging my head for two days I was able to produce this bizarre code and looks like it is working!

The goal was to hit test CAShapeLayer. The CAShapeLayer is moving on the screen, so the shape is not in constant place. Hittesting the CGPath currentPoint is not straightforward.

Feel free to add any input...

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

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

    CGAffineTransform transf = CGAffineTransformMakeTranslation(-shapeLayer.position.x, -shapeLayer.position.y); 

    if(CGPathContainsPoint(shapeLayer.path, &transf, p, NO)){    

       // the touch is inside the shape  
    }   

}
1
votes

See below - override the CAShapeLayers hitTest() method

    // CAShapeLayer path coordinates are relative to the layer
    // so when hit testing to see if the mouse click is inside the path
    // convert the point to layer coordinates like so
    // This assumes the received coordinates are in the layers superlayer coordinates
    // Usually so if they come from a mouse click

    override func hitTest(_ p: CGPoint) -> CALayer? {
        
        let lp = self.convert(p, from: superlayer)
        
        let result = path?.contains(lp) ?? false
        
        return result ? self : nil
            
    }