I’ve been trying to solve this for ages, and this discussion helped me a lot. I just wanted to summarize everything posted here, and what I learned from Apple’s docs, to make it easier for people looking out for help in the future.
The main difficulty of this challenge is to make the shadow appear only on the outside of the CALayer, but having that our boxLayer’s contents are semi-transparent, the shadow ends up showing underneath it as well.
Apple’s documentation explicitly mentions this exact problem, suggests a solution and even shows a picture example with the exact solution we are looking for, but doesn’t go on to show how to do it . See developer.apple.com/library/ios … 4-CH10-SW1.
[size=85]Notice how the red shadow doesn’t show through the teal background, even though it is semi-tranparent. This is exactly what we want:
There’s one thing thought that the article above makes clear: since we have rounded corners, we wouldn’t want the image content go off that boundary. This problem isn’t immediately noticeable because, in the example given in the book, the author is using an image with transparent background, but if you replace it with a regular rectangular image, it will become evident:
To solve this, you have to set the CALayer maskToBounds property to YES:
However, turning this on will also clip the shadow:
So, to fix this new problem you have to put your CALayer inside another CALayer, with the exact same size as the first, and apply the shadow to this new layer instead. This way the original CALayer will clip its contents to its rounded rect bounds, but will not affect the shadow created by its superLayer.
[size=85]Changes to the code (we now have another CALayer encapsulating the first one):
[/size][code]boxLayer = [[CALayer alloc] init];
[boxLayer setBounds:CGRectMake(0, 0, 85, 85)];
[boxLayer setPosition:CGPointMake(160, 100)];
[boxLayer setShadowColor: [[UIColor blackColor] CGColor]];
[boxLayer setShadowOffset:CGSizeMake(10, 10)];
CALayer *box2 = [[CALayer alloc] init];
[box2 setBounds:CGRectMake(0, 0, 85, 85)];
[box2 setPosition:CGPointMake(42.5, 42.5)];
[box2 setCornerRadius:[boxLayer bounds].size.width/4];
UIColor *reddish = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.5];
CGColorRef cgReddish = [reddish CGColor];
UIImage *layerImage = [UIImage imageNamed:@"test.png"];
CGImageRef image = [layerImage CGImage];
[box2 setContents:(__bridge id)image];
[box2 setContentsRect:CGRectMake(-0.1, -0.1, 1.2, 1.2)];
[[self layer] addSublayer];[/code]
The last part of this puzzle was given to us by our friend Gorthmog: instead of giving our layer a semi-transparent red background, we should give it a full opaque background. Then we set it’s superLayer’s (the one that creates the shadow) opacity to anything < 1. This will create a shadow only on the outside of our content CALayer, while keeping its semi-transparent content intact :
[size=85]Code changes (also going back to the original image, now that we know we are clipping it appropriately):
[box2 setBackgroundColor:[[UIColor redColor] CGColor]];
@Gorthmog: Thanks for posting your solution! I tested removing the [layer setShouldRasterize:YES] and it still works perfectly, so the secret was just the full opaque background.
@pschluet: If you want the image content of your layer to respect the rounded corners, then you do need a second layer. If you are not using rounded corners, then you don’t need to clip its contents, and your solution would be correct.
Hope it helps.