之前曾写过一篇如何通过分类设置纯色图片\带圆角图片等的文章,连接:
http://www.jianshu.com/writer#/notebooks/5359278
上述方法比较简单粗暴,除了在设置圆角图片时避免了离屏渲染的问题,其他性能上并未做优化处理,接下来,继续整理一下对设置图片性能提升方面的处理,这里我们将会借助模拟器为我们提供的两种模式来对项目进行优化:Color Misaligned Images和 Color Blended Layers .
Tableview是日常开发中十分常见的控件,仔细观察会发现,为什么有的app在滚动时,界面非常流畅,而有的app则显得卡顿呢?
先来分析下,在Tableview中往往只有两部分的操作:
1.获取重用Cell
2.根据模型设置数据
通过缓存池和Cell的重用机制,很大程度上缓解了CPU的开销和内存的浪费
而我们需要考虑的就是在获取到重用Cell后,在给Cell设置数据时,减少其对控件的处理来提升性能
Cell中最常见的控件无非就是UIImageView和UILabel,而我们又能够做哪些优化以及系统在设置数据环节还额外的做了哪些处理呢?
首先一点,我们在使用图片时,往往一些图片和视图上的UIImageView尺寸不符,这就导致了图片在设置时,系统要为其做拉伸/缩放处理,如果一个Tableview中需要系统做拉伸/缩放处理的UIImageView很多,就会在一定程度上的出现卡顿的现象
前面都是铺垫,接下来才是这篇文章的重点:如何高性能的设置图片
首先,借助模拟器,我们可以判断出,哪些控件的尺寸是系统做过处理的
模拟器菜单栏中-->Debug-->Color Misaligned Images
如果勾选后,图像显示为黄色,代表当前图片的大小和使用的ImageView大小不一致,做过拉伸处理
代码演示:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
imageView.center = self.view.center;
[self.view addSubview:imageView];
// 直接按照ImageView的尺寸来设置图片
// 图片的真实尺寸: 640 × 480 pixels
imageView.image = [UIImage imageNamed:@"路飞.jpg"];
效果图:
开启 Color效果图:
补充说明:使用模拟器判断缩放时,需要注意模拟器自身的尺寸是否进行过缩放(cmd+1 - 100%...),这里考虑到显示器尺寸,为了让100%显示的模拟器完全显示在屏幕上(如果是5.5寸,100%显示屏幕上显示不全),考虑到呈现效果,使用了6s模拟器
解决方式:
按照UIImageView的尺寸,提前对图片进行拉伸/缩放处理,设置一致后再将图片设置给UIImageView
- (UIImage *)js_imageWithSize:(CGSize)size{
// 开启图形上下文
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
// 设置rect
CGRect rect = CGRectMake(0, 0, size.width, size.height);
// 将图片绘制到图形上下文
[self drawInRect:rect];
// 从图形上下问获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
// 返回图片
return image;
}
效果图:
这样便解决了图片与UIImageView尺寸不一致,系统对齐进行拉伸操作而带来的性能上的浪费
尺寸不一致的问题我们处理完了,设置圆角也是同理
其次,使用颜色的时候,尽可能不要使用带透明度的颜色(不要设置Alpha值)
假设在视图控制器的View中放置一个BlueView和一个GreenView
因视图上产生叠加,默认不设置透明度,处于上层的BlueView就会将叠加部分的GreenView遮挡;
而这时我将处于上层的BlueView设置了透明度,这样GreenView与BlueView叠加部分就会被透出来
在整个屏幕绘制过程中:
1.先将BlueView没有被叠加部分绘制出来
2.其次计算BlueView和BlueView叠加部分区域大小
3.然后再根据BlueView颜色的透明度,叠加了GreenView颜色计算产生的颜色,使用计算的颜色值绘制叠加部分
4.绘制BlueView叠加部分外的区域
从分析结果来看,如果使用了透明度颜色也会严重影响性能,所以在开发中如果不是刚需,尽量避免使用透明度颜色
既然分析出了需要优化改进的点,那么我们继续使用模拟器自带的功能来对项目进行改进
模拟器菜单栏中-->Debug-->Color Blended Layers
勾选"Color Blended Layers"
这是模拟器为我们提供的一种混合模式
通过混合模式我可以判断出哪些部分进行过透明处理
如果控件部分显示了红色,则表明进行过透明处理,可以对其进行性能上的优化
这里先直接使用上篇文章中设置圆角图片的类方法来演示下效果:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
imageView.center = self.view.center;
[self.view addSubview:imageView];
// 方式一: 设置圆角图片
UIImage *image = [UIImage imageNamed:@"路飞.jpg"];
imageView.image = [UIImage js_imageWithOriginalImage:[image js_imageWithSize:CGSizeMake(200, 200)]];
注意:在UIKit中UILabel的性能是比较差的,但又是不得不用的(也可以自己写一个或者使用其他的开源框架),除了UILabel外,其他的出现混合模式的视图,能处理的我们都应该尽可能的进行优化
代码优化:
// 生成圆角图片(优化后)
- (UIImage *)js_cornerImageWithSize:(CGSize)size{
// 开启图形上下文
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
// 设置rect
CGRect rect = CGRectMake(0, 0, size.width, size.height);
// 计算半径
CGFloat cornerRadius = MIN(size.width, size.height) * 0.5;
// 设置圆形路径并切割
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius] addClip];
// 将原始图片绘制到图形上下文中
[self drawInRect:rect];
// 从图形上下获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
// 返回圆形图片
return image;
}
效果图:
不再显示红色
由于开启图形上下文时参数设置为YES(不透明),所以显示有黑边,为了高性能的回去圆形图片,避免设置透明色,我们来传递一个填充色来解决黑边的问题
// 生成圆角图片(优化后)
- (UIImage *)js_cornerImageWithSize:(CGSize)size fillClolor:(UIColor *)fillColor{
// 开启图形上下文
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
// 设置rect
CGRect rect = CGRectMake(0, 0, size.width, size.height);
// 设置填充色
[fillColor set];
UIRectFill(rect);
// 计算半径
CGFloat cornerRadius = MIN(size.width, size.height) * 0.5;
// 设置圆形路径并切割
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius] addClip];
// 将原始图片绘制到图形上下文中
[self drawInRect:rect];
// 从图形上下获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
// 返回圆形图片
return image;
}
效果图:
这样我们在设置图片时,即没有拉伸,也没有做透明度的混合,当我们在使用Tableview滚动时,就不再会需要对图片执行额外的处理
最后,相对于CPU的处理速度来讲,整个图片处理的过程相当于一个耗时操作,所以我们可以将整个图片过程放到异步执行
// 生成圆角图片(优化后)
- (void)js_cornerImageWithSize:(CGSize)size fillClolor:(UIColor *)fillColor completion:(void(^)(UIImage *img))completion{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 获取开始时间
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
// 开启图形上下文
UIGraphicsBeginImageContextWithOptions(size, YES, 0.0);
// 设置rect
CGRect rect = CGRectMake(0, 0, size.width, size.height);
// 设置填充色
[fillColor set];
UIRectFill(rect);
// 计算半径
CGFloat cornerRadius = MIN(size.width, size.height) * 0.5;
// 设置圆形路径并切割
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius] addClip];
// 将原始图片绘制到图形上下文中
[self drawInRect:rect];
// 从图形上下获取图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图形上下文
UIGraphicsEndImageContext();
// 获取结束时间
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
//打印时间
NSLog(@"%f",end - start);
// 返回圆形图片
// return image;
// 返回主线程
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(image);
}
});
});
}
这样我们就通过模拟器判断了程序的执行性能并进行了性能上的优化
示例代码:
https://github.com/ShenYj/Demos