王zi如评测 之 iPhone圆角对手机FPS的影响

2016-09-01

首先标题是随性创造,如有雷同纯属巧合!为什么要写这篇文章?最近在搞xamarin开发,做个控件圆角难做得蛋疼。很是怀念OC的实现。然后一个兴起做了这个评测。
本评测主要是探讨在多种方式下实现iOS圆角的实现,对FPS(frames per second ==帧每秒)的影响。

先来来个对比:请问案例一卡顿还是案例二卡顿?

案例一

案例二
答案就是:答案自找

ok,先来说说画圆角的方式:

##1. 使用layer,这应该是我们最常用的方法了,两代码解决战斗。

= 22;
1
view.layer.masksToBounds = YES;

上图:
layer方式画圆

FPS

可以从图中知道,在滑动tableview时,fps只有18、19(正常60)。这种状态有个俗语:“卡出翔”。
简略代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"threeCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"threeCell"];
...
...
UIImageView * view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"hehe"]];
view.frame = CGRectMake(0, 0, 44, 44);
view.layer.cornerRadius = 22;
view.layer.masksToBounds = YES;
// view.layer.shouldRasterize = YES;
// view.layer.rasterizationScale = [UIScreen mainScreen].scale;
[cell addSubview:view];
...

我们得想办法优化ho,然后Google了下就有了上面注释的两行代码。
想必大家也留意到这两行代码

1
2
// view.layer.shouldRasterize = YES;
// view.layer.rasterizationScale = [UIScreen mainScreen].scale;

加上以后,大家可以看看效果。
11-12的fps
更加卡了,怎么办呢?为啥呢?

其实这两句话不应该放在这个地方,应该在cell.layer上面添加。

1
2
cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;

修改了地方以后,我们再看看效果。
cell.layer.shouldRasterize

添加这两句代码后,能将你的帧数提高到50以上,

= YES```会使视图渲染内容被缓存起来,下次绘制的时候可以直接显示缓存。但这的前提是得保证内容不变的情况下。如果你的图片是变化的,这可能也会对性能造成影响。
1
2
##2.drawrect

UIImage * myImage = [UIImage imageNamed:@”hehe”];
CGContextRef ctx = UIGraphicsGetCurrentContext();

CGContextSaveGState(ctx);//保存上下文
CGContextAddEllipseInRect(ctx,CGRectMake(105, 5, 40, 40));//因为只要确定了矩形框,圆或者是椭圆就确定了。
CGContextClip(ctx);

CGContextTranslateCTM(ctx, 0, 50);
CGContextScaleCTM(ctx, 1, -1);
CGContextDrawImage(ctx, CGRectMake(100, 0, 50, 50), [myImage CGImage]);
CGContextRestoreGState(ctx);//恢复状态后,就不会受裁剪的影响,不然只能在剪裁
1
2
3
4
5
6
7
8
![drawrect.gif](http://upload-images.jianshu.io/upload_images/271180-5b3ab5f2a80b168b.gif?imageMogr2/auto-orient/strip)
![drawrect.png](http://upload-images.jianshu.io/upload_images/271180-11d55ece3f57f7eb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
从图中可以分析得出,使用drawrect方式来实现圆角,FPS会比较高,基本不会卡顿。而CPU的使用情况也是可以接受的。
##3.UIBezierPath,贝塞尔曲线

UIImageView * view = [[UIImageView alloc] init];
view.frame = CGRectMake(200, 0, 44, 44);
[cell addSubview:view];

UIImage * image = [UIImage imageNamed:@"hehe"];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(44, 44), NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:22] addClip];
[image drawInRect:view.bounds];
view.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
1
2
3
4
5
![bezi.gif](http://upload-images.jianshu.io/upload_images/271180-a5d15b73e4b57ab3.gif?imageMogr2/auto-orient/strip)
![bezi.png](http://upload-images.jianshu.io/upload_images/271180-38282b91f39a912f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
使用贝塞尔曲线来做这个功能,CPU使用情况比drawrect的方式要高一些,但也没有很高的状态。

dispatch_queue_t Cricle = dispatch_queue_create(“ImageCreate1”, NULL);
dispatch_async(Cricle, ^{
UIImage image = [UIImage imageNamed:@”hehe”];
UIGraphicsBeginImageContextWithOptions(CGSizeMake(44, 44), NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:view.bounds cornerRadius:22] addClip];
[image drawInRect:view.bounds];
UIImage
doneImage = UIGraphicsGetImageFromCurrentImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
view.image = doneImage;
});
UIGraphicsEndImageContext();
});

1
2
3
4
5
使用一个子线程去渲染图片,然后在主线程更新UI,这方法在如果只渲染一张图片速度还是挺快的,cpu和fps都没啥问题。但一个cell中有4个这样的图片,手机就不行了。
![fps&CPU](http://upload-images.jianshu.io/upload_images/271180-77b8e18503bfe53a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
CPU处于满载状态,一开始fps还有50+,慢慢就变成只有20-30了。所以不推荐这个,还是简单粗暴比较好。(若是我打开姿势不对,请立马留意)
##4.Mask,很有趣的方法

cell.layer.shouldRasterize = YES;
cell.layer.rasterizationScale = [UIScreen mainScreen].scale;


UIImageView * view = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@”hehe”]];
view.frame = CGRectMake(0, 0, 44, 44);
[cell addSubview:view];

CALayer *maskLayer        = [CALayer layer];
maskLayer.frame           = view.bounds;
maskLayer.contents        = (id)[UIImage imageNamed:@"xingxing"].CGImage;
view.layer.mask = maskLayer;

```
先看图
mask.gif

mask.layer.png
这实现效果和layer.cornerRadius相类似,但Mask的性能比layer.cornerRadius要低。不过优点是你可以将你的图片变成任何形状的图形。

总结:
1.如果你的内容不多变,并且当前页面不卡顿。可以使用layer.cornerRadius+ layer.shouldRasterize来实现。
2.如果你当前页面需要优化性能,并且内容是变化的,推荐使用贝塞尔曲线来实现。
3.drawrect方法会造成离屏渲染,当点击cell或者页面有内容更新的时候就会重新调用,会造成不必要的性能损耗。

ps:有些人会在图片上面覆盖一个镂空圆形图片的方法可以实现圆形头像效果,这个虽然是个极为高效的方法。但我的想法是——

你在逗我!

这里是github连接:
https://github.com/ouzhenxuan/DrawCircle
喜欢请点个赞。