Search code examples
ioscalayer

Masking CALayer shadow to outside of rect only


How would I mask CALayer with shadow, so that the shadow is only outside the path? I don't want shadow behind a transparent view.

shadowLayer.shadowOffset = CGSizeMake(x, y);
shadowLayer.shadowRadius = radius;
shadowLayer.shadowOpacity = opacity;
shadowLayer.shadowColor = color.CGColor;
shadowLayer.shadowPath = [UIBezierPath bezierPathWithRect:view.bounds].CGPath;

Thank you.


Solution

  • I will answer my own question. Adding a new shadow layer for view. This should work for any shadowPath, if set correctly.

    float radius = 8;
    float opacity = 0.5f;
    float x = 4;
    float y = 6;
    UIColor *color = [UIColor blackColor];
    
    // Shadow layer
    CALayer *shadowLayer = [CALayer layer];
    shadowLayer.shadowOffset = CGSizeMake(x, y);
    shadowLayer.shadowRadius = radius;
    shadowLayer.shadowOpacity = opacity;
    shadowLayer.shadowColor = color.CGColor;
    shadowLayer.shadowPath = [UIBezierPath bezierPathWithRect:view.frame].CGPath; // Or any other path
    
    // Shadow mask frame
    CGRect frame = CGRectInset(view.layer.frame, -2*radius, -2*radius);
    frame = CGRectOffset(frame, x, y);
    
    // Translate shadowLayer shadow path to mask layer's coordinate system
    CGAffineTransform trans = CGAffineTransformMakeTranslation(-view.frame.origin.x-x+2*radius,
                                                               -view.frame.origin.y-y+2*radius);
    
    // Mask path
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, nil, (CGRect){.origin={0,0}, .size=frame.size});
    CGPathAddPath(path, &trans, shadowLayer.shadowPath);
    CGPathCloseSubpath(path);
    
    // Mask layer
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = frame;
    maskLayer.fillRule = kCAFillRuleEvenOdd;
    maskLayer.path = path;
    
    shadowLayer.mask = maskLayer;
    
    [view.layer.superlayer insertSublayer:shadowLayer below:view.layer];