UIImageView高效加个圆角
一般通过clipsToBounds和layer.cornerRadius会强制Core Animation提前渲染屏幕的离屏绘制,影响性能。
通过贝塞尔曲线切割图片实现path.addclip();
2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
4、直接调用setNeedsDisplay
drawRect方法使用注意点:
2、若使用calayer绘图,只能在drawInContext: 中(类似于drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法
3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来调用setNeedsDisplay实时刷新屏幕
2、addSubview会触发layoutSubviews。
3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。
4、滚动一个UIScrollView会触发layoutSubviews。
5、旋转Screen会触发父UIView上的layoutSubviews事件。
6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件。
使用透明view会引起blending,在iOS的图形处理中,blending主要指的是混合像素颜色的计算。最直观的例子就是,我们把两个图层叠加在一起,如果第一个图层的透明的,则最终像素的颜色计算需要将第二个图层也考虑进来。这一过程即为Blending。
8、离屏渲染的问题:
在VSync信号到来后,系统图形服务会通过CADisplayLink等机制通知App,App主线程开始在CPU中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后CPU会将计算好的内容提交到GPU去,由GPU进行变换、合成、渲染。随后GPU会把渲染结果提交到帧缓冲区去,等待下一次VSync信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个VSync时间内,CPU或者GPU没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。
在开发中,CPU和GPU中任何一个压力过大,都会导致掉帧现象,所以在开发时,也需要分别对CPU和GPU压力进行评估和优化。
CPU
加载资源,对象创建,对象调整,对象销毁,布局计算,Autolayout,文本计算,文本渲染,图片的解码, 图像的绘制(Core Graphics)都是在CPU上面进行的。
GPU
GPU是一个专门为图形高并发计算而量身定做的处理单元,比CPU使用更少的电来完成工作并且GPU的浮点计算能力要超出CPU很多。
GPU的渲染性能要比CPU高效很多,同时对系统的负载和消耗也更低一些,所以在开发中,我们应该尽量让CPU负责主线程的UI调动,把图形显示相关的工作交给GPU来处理,当涉及到光栅化等一些工作时,CPU也会参与进来,这点在后面再详细描述。
相对于CPU来说,GPU能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合(合成)并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。
异步渲染
在子线程绘制,主线程渲染。例如 VVeboTableViewDemo
按需加载
-
局部刷新,刷新一个cell就能解决的,坚决不刷新整个section或者整个tableView,刷新最小单元元素。
-
利用runloop提高滑动流畅性,在滑动停止的时候再加载内容,像那种一闪而过的(快速滑动),就没有必要加载,可以使用默认的占位符填充内容。
当你使用UIImageView在加载一个视图的时候,这个视图虽然依然有CALayer,但是却没有申请到一个后备的存储,取而代之的是使用一个使用屏幕外渲染,将CGImageRef作为内容,并用渲染服务将图片数据绘制到帧的缓冲区,就是显示到屏幕上,当我们滚动视图的时候,这个视图将会重新加载,浪费性能。所以对于使用-drawRect:方法,更倾向于使用CALayer来绘制图层。因为使用CALayer的-drawInContext:,Core Animation将会为这个图层申请一个后备存储,用来保存那些方法绘制进来的位图。那些方法内的代码将会运行在CPU上,结果将会被上传到GPU。这样做的性能更为好些。
静态界面建议使用-drawRect:的方式,动态页面不建议。
离屏渲染的优化建议
-
使用ShadowPath指定layer阴影效果路径。
-
使用异步进行layer渲染(Facebook开源的异步绘制框架AsyncDisplayKit)。
-
设置layer的opaque值为YES,减少复杂图层合成。
-
尽量使用不包含透明(alpha)通道的图片资源。
-
尽量设置layer的大小值为整形值。
-
直接让美工把图片切成圆角进行显示,这是效率最高的一种方案。
-
很多情况下用户上传图片进行显示,可以在客户端处理圆角。
-
使用代码手动生成圆角image设置到要显示的View上,利用UIBezierPath(Core Graphics框架)画出来圆角图片。
不可为:
- 为图层设置遮罩(layer.mask)
- 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
- 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
- 为图层设置阴影(layer.shadow *)。
可为
- 使用CAShapeLayer和UIBezierPath设置圆角
- 通过设置shadowPath来优化性能,能大幅提高性能。
- 当我们需要圆角效果时,可以使用一张中间透明图片蒙上去
- 使用异步进行layer渲染(Facebook开源的异步绘制框架AsyncDisplayKit)
- 设置layer的opaque值为YES,减少复杂图层合成
- 设置layer的opaque值为YES,减少复杂图层合成
- 尽量设置layer的大小值为整形值
那哪些情况会Offscreen Render呢?
1) drawRect 2) layer.shouldRasterize = true; 3) 有mask或者是阴影(layer.masksToBounds, layer.shadow*);3.1) shouldRasterize(光栅化)3.2) masks(遮罩)3.3) shadows(阴影)3.4) edge antialiasing(抗锯齿)3.5) group opacity(不透明) 4) Text(UILabel, CATextLayer, Core Text, etc)...
注:layer.cornerRadius,layer.borderWidth,layer.borderColor并不会Offscreen Render,因为这些不需要加入Mask。
一方面来说,一部分(通过drawRect并且使用任何CoreGraphics来实现的绘制,或使用CoreText[其实就是使用CoreGraphics]绘制)的确涉及到了“离屏绘制”,但是这和我们通常说的那种离屏绘制是有区别的。当你实现drawRect方法或者通过CoeGraphics绘制的时候,其实你是在使用CPU绘制。并且这个绘制的过程会在你的App内同步地进行。基本上你只是调用了一些向位图缓存内写入一些二进制信息的方法而已。
而另外一种形式离屏绘制是发生在绘制服务(是独立的处理过程)并且同时通过GPU执行(这里就不是像前面提到的用CPU了)。当OpengGL的绘制程序在绘制每个layer的时候,有可能因为包含多子层级关系而必须停下来把他们合成到一个单独的缓存里。你可能认为GPU应该总是比CPU牛逼一点,但是在这里我们还是需要慎重的考虑一下。因为对GPU来说,从当前屏幕(on-screen)到离屏(off-screen)上下文环境的来回切换(这个过程必须flush管线和光栅),代价是非常大的。因此对一些简单的绘制过程来说,这个过程有可能用CoreGraphics,全部用CPU来完成反而会比GPU做得更好。所以如果你正在尝试处理一些复杂的层级,并且在犹豫到底用-[CALayer setShouldRasterize:]还是通过CoreGraphics来绘制层级上的所有内容,唯一的方法就是测试并且进行权衡。
测试工具:
-
Core Animation,Instruments里的图形性能问题的测试工具。
-
view debugging,Xcode 自带的,视图层级。
-
reveal,视图层级。