在iOS上实现阴影的最快方法是什么?

37

QuartzCore .layer.shadow的性能不佳。每次发生变化时都需要重新渲染, 导致一切都变得缓慢。

Coregraphics渐变(用于单向阴影)- 看起来不对。如果你的渐变从0.3 alpha到0,它会产生一些奇怪的效果,你可以“看到”它停止。它看起来既不漂亮也不自然。也许它没有被抖动,但我确定我听说过核心图形渐变是这样的。这很奇怪,我不知道。

Coregraphics阴影 - 设置它们需要一段时间来渲染,但除此之外表现良好。问题在于你等待一个视图出现的那一秒钟,因为它必须先渲染它的阴影。

所以我肯定有什么遗漏了。是否有另一种方法既看起来正确,又在渲染时间和性能方面速度快?

2个回答

106

添加shadowPath可以大大提高性能。以下示例假设您只想在视图的侧面上显示阴影。

CGPathRef path = [UIBezierPath bezierPathWithRect:view.bounds].CGPath;
[view.layer setShadowPath:path];

编辑: 默认情况下,CALayer 在动画期间会绘制阴影。以下代码允许您将阴影缓存为位图并重复使用,而不是重新绘制它:

self.view.layer.shouldRasterize = YES;
// Don't forget the rasterization scale
// I spent days trying to figure out why retina display assets weren't working as expected
self.view.layer.rasterizationScale = [UIScreen mainScreen].scale;

1
rasterizationScale 也曾让我困扰了一段时间!你可能会想它会基于屏幕比例自动设定默认值,但实际并非如此。 :-/ - Dermot
我有一个带有锯齿边缘的部分透明PNG图像需要进行动画处理(当然还要加上阴影)。它占据了屏幕的2/3大小,因此包含了相当多的像素。这个解决方案仍然完美无缺地工作着。 :) - toblerpwn
你是个天才。@chklbumper,你在测试时使用的是旧款 iPhone 吗?iPhone 5 可以快速呈现 shadowOffset 阴影,但 iPhone 4 在绘制 shadowOffset 阴影时会出现延迟和闪烁。 - Nate Symer
太棒了 @aryaxt 谢谢 - Vaibhav Limbani
rasterizationScale 可以带来更好的性能,但并非最佳。我在我的自定义键盘中使用它,但仍然会在开始时有一点卡顿。 - TomSawyer
显示剩余3条评论

10

我经常看到人们使用 HUGE 性能影响的视图层来创建圆角或阴影效果。就像这样:

[v.layer setCornerRadius:30.0f];
[v.layer setBorderColor:[UIColor lightGrayColor].CGColor];
[v.layer setBorderWidth:1.5f];
[v.layer setShadowColor:[UIColor blackColor].CGColor];
[v.layer setShadowOpacity:0.8];
[v.layer setShadowRadius:3.0];
[v.layer setShadowOffset:CGSizeMake(2.0, 2.0)];
.....

这将会对性能产生巨大的影响,特别是阴影效果。将这样的视图放在UITableView(或者移动任何东西)中会创建一种类似于Android的滚动体验,你不想要那样的结果。如果需要对视图进行动画或移动,请避免以任何方式创建圆角或投影阴影等效果!
认识核心绘图:我创建了一个简单的UIView子类来向您展示如何以稍微不同的方式实现相同的结果。它使用Core Graphics来绘制视图,并且与上面的代码相比,它不会影响性能。
以下是绘图代码:
- (void)drawRect:(CGRect)rect
{
   CGContextRef ref = UIGraphicsGetCurrentContext();

  /* We can only draw inside our view, so we need to inset the actual 'rounded content' */
  CGRect contentRect = CGRectInset(rect, _shadowRadius, _shadowRadius);

  /* Create the rounded path and fill it */
  UIBezierPath *roundedPath = [UIBezierPath bezierPathWithRoundedRect:contentRect cornerRadius:_cornerRadius];
  CGContextSetFillColorWithColor(ref, _fillColor.CGColor);
  CGContextSetShadowWithColor(ref, CGSizeMake(0.0, 0.0), _shadowRadius, _shadowColor.CGColor);
  [roundedPath fill];

  /* Draw a subtle white line at the top of the view */
  [roundedPath addClip];
  CGContextSetStrokeColorWithColor(ref, [UIColor colorWithWhite:1.0 alpha:0.6].CGColor);
  CGContextSetBlendMode(ref, kCGBlendModeOverlay);

  CGContextMoveToPoint(ref, CGRectGetMinX(contentRect), CGRectGetMinY(contentRect)+0.5);
  CGContextAddLineToPoint(ref, CGRectGetMaxX(contentRect),   CGRectGetMinY(contentRect)+0.5);
  CGContextStrokePath(ref);
 }

请查看这篇博客:http://damir.me/rounded-uiview-with-shadow-the-right-way,它与带有阴影的圆角UIView有关。

这篇博客中的Git存储库链接似乎不再起作用了。我该如何在UICollectionViewCell上使用它? - trdavidson
1
将上述drawrect添加到您的UICollectionViewCell子类中@trdavidson - Omar Freewan
我已经玩弄这段代码一段时间了,遇到了以下问题:单元格中的子视图没有被剪裁,因此角落里的视图仍然重叠在圆角上。此外,在滚动时,似乎有些单元格被“忽略”,根本没有得到阴影层。你认为可能是什么原因引起的? - trdavidson
请确保在数据源代理方法中调用 [yourcell setNeedsToDisplay],以查看您的问题是否仍然存在。@trdavidson - Omar Freewan
从性能角度来看,从bundle加载一个小于1kb的UIImage还是使用UIBezierPath绘制更好? - Leonardo Cavalcante

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接