Search code examples
calayermaskhittest

Setting mask on CALayer breaks hitTest


I have a CALayer with hitTesting that works fine. The code is something like this:

- (void)setup
{
    _maskLayer = [CAShapeLayer layer];
    _maskLayer.fillColor = [UIColor whiteColor].CGColor;
    _maskLayer.path = somePath;

    _theLayer.frame = CGRectMake(0, 0, size.width, size.height);
    // Taps work fine if we do not apply the mask
    // _theLayer.mask = _maskLayer;
}

- (IBAction)tapOnView:(UITapGestureRecognizer *)sender
{
    CGPoint point = [sender locationInView:theView];
    CALayer *subLayer = [_theLayer.presentationLayer hitTest:point].modelLayer;

    if (subLayer != _theLayer && subLayer != nil) {
        // Do something with the sublayer which the user touched
    }
}

The problem is that if I uncomment the line with the mask, my hit testing always returns nil. Why does the mask kill hit testing? It appears correct visually.


Solution

  • It turns out that hit testing requires the point to be inside the bounds of the CALayer, but what is not documented is that it also has to be inside the bounds of the mask. So I changed my code like this and it worked:

        _theLayer.frame = CGRectMake(0, 0, size.width, size.height);
    
        // Apply a mask
        _maskLayer.frame = _theLayer.frame;
        _theLayer.mask = _maskLayer;
    

    It took me a few hours to figure this out, so I wanted to share.