CALayer - Shadow causes a performance hit?
Asked Answered
D

6

36

So I am doing some custom animations on my navigationcontroller and the way it pushes and pops the viewControllers.

Everything runs smooth. As soon as I add the following code (In a subclass of UINavigationController), I face a huge performance hit. After adding a shadow all animations become very laggy. Is this expected or am I doing something wrong in the code?

// This code gets called once during NavigationController initialization.
[self.view setClipsToBounds:NO];
[self.view.layer setCornerRadius:5];
[self.view.layer setShadowOffset:CGSizeMake(0, 20)];
[self.view.layer setShadowColor:[[UIColor yellowColor] CGColor]];
[self.view.layer setShadowRadius:20.0];
[self.view.layer setShadowOpacity:1];

EDIT:

Changed my shadow radius to 1 and it's still slow

Diagnose answered 3/4, 2012 at 17:2 Comment(1)
For all it's worth, this is covered explicitly in WWDC 2010 Session 425: Core Animation in Practice, Part 2 here.Conch
U
54

You should expect a slowdown from adding a shadow. A shadowRadius of 20 is very high and will be especially slow.

The other key to improve shadow rendering speed: set the shadowPath property. It can help dramatically.

Unexpressed answered 3/4, 2012 at 17:15 Comment(4)
setting ShadowPath did the trick, My shadow radius is 10 and it still performs well.Diagnose
@NicolasManzini More tips for shadow performance: #10133609Diagnose
it's much efficient way that I test... idea with shouldRasterize work not so quick as it can be needed. this answer is much better! don't understand why @Matsu answer gain so much votes...Natoshanatron
shadowPath did the trick. performance feels a million times better!Greenfield
M
89
self.view.layer.shouldRasterize = YES;
self.view.layer.rasterizationScale = UIScreen.mainScreen.scale;

I was recently having some issues with slow CALayer shadows, and that simple line of code fixed up everything for me!

Matsu answered 30/4, 2012 at 18:14 Comment(5)
awesome, this makes it perform even better, now I can have any size of shadowRadius without having a performance hitDiagnose
This will ignore the retina images that you may have for you annotation. To fix this, add the following line: self.view.layer.rasterizationScale = [UIScreen mainScreen].scale;Sheriff
I'm having a similar issue with a tableView where each cell contains a few UILabels as well as a view with rounded corners. I added a shadow to the layer that contains the tableView and the scrolling becomes choppy. However, setting shouldRasterize = YES resolved the choppiness but pixelated the view (visibly blurry) so it's not an acceptable solution. I found using shadowPath instead of shadowOffset did the trick.Radiance
it could be worse if the view varies. shouldRasterize = YES is only a good solution when the view does not change, so bitmap is genereated once once and cached and reused by UIScrollViewUltramodern
Please note that this increases your RAM usageUkulele
U
54

You should expect a slowdown from adding a shadow. A shadowRadius of 20 is very high and will be especially slow.

The other key to improve shadow rendering speed: set the shadowPath property. It can help dramatically.

Unexpressed answered 3/4, 2012 at 17:15 Comment(4)
setting ShadowPath did the trick, My shadow radius is 10 and it still performs well.Diagnose
@NicolasManzini More tips for shadow performance: #10133609Diagnose
it's much efficient way that I test... idea with shouldRasterize work not so quick as it can be needed. this answer is much better! don't understand why @Matsu answer gain so much votes...Natoshanatron
shadowPath did the trick. performance feels a million times better!Greenfield
A
15

Using shadowPath instead of shadowOffset.

theView.layer.shadowPath = [UIBezierPath bezierPathWithRect:theView.bounds].CGPath;

Check this post: iphone - Animation's performance is very poor when view's shadow is on

Acyclic answered 4/3, 2013 at 6:30 Comment(5)
Worked like a charm, better than shouldRasterize which blurred the view and all sublayers.Radiance
it's much efficient way that I test... idea with shouldRasterize work not so quick as it can be needed. this answer is much better! don't understand why @Matsu answer gain so much votes...Natoshanatron
shadowPath is pretty good and fast, but how can keep update frame in autolayout?Ucayali
This is only if you don't have rounded corners or transparency in your views. Not every views is rectangular. shouldRasterize is more generic and we don't have to deal with any bezier paths as said.Sincerity
@Ucayali do it in view's layoutSubviews, maybe you need subclass some viewsTuchman
N
2

Yes, shadow's are very expensive (especially a shadow that big -- play with the radius and you'll notice it makes a huge difference in the degree of slowdown you experience). One way to improve performance is to render it once to a CGImageContext and just display that image instead of having the layer re-render the shadow every time it redraws (but this doesn't work if the shadow needs to animate or something).

Newspaper answered 3/4, 2012 at 17:9 Comment(3)
You think it would perform better to use an actual png and add it to my UIView?Diagnose
If that's a possibility (ie you know the shape/size/whatever of the shadow beforehand) then it would definitely give you better performance. Blitting images is much faster than rendering shadows.Newspaper
Prerendered to an image will work as long as yuo don't need to change the shadow shape.Cannoneer
T
0

Swift 5.3. add this code.

myView -> UIView, collectionViewCell or tableViewCell can be.

myview.layer.shadowPath = UIBezierPath(rect: cell.bounds).cgPath
myview.layer.shouldRasterize = true
myview.layer.rasterizationScale = UIScreen.main.scale
Truancy answered 26/8, 2020 at 9:14 Comment(0)
K
0

I'm coding app with "Neumorphism" style, so much shadow and app so laggy. But use this code below, app very smooth.

viewHasShadow.layer.shouldRasterize = true
viewHasShadow.layer.rasterizationScale = UIScreen.main.scale
Kiva answered 10/5, 2021 at 10:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.