美文网首页
保持界面流畅技巧

保持界面流畅技巧

作者: liboxiang | 来源:发表于2018-12-21 17:31 被阅读14次

https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
https://objccn.io/issue-3-1/

http://www.imlifengfeng.com/blog/?p=593
https://robots.thoughtbot.com/designing-for-ios-graphics-performance

tumblr_mdyk43n5jr1qatp4h.png
Snip20180524_2.png

离屏渲染

  • On-Screen Rendering (当前屏幕渲染)
    指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。
  • Off-Screen Rendering (离屏渲染)
    指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。
  • Core Graphics 的绘制 API 的确会触发离屏渲染,但不是那种 GPU 的离屏渲染。使用 Core Graphics 绘制 API 是在 CPU 上执行,触发的是 CPU 版本的离屏渲染。
CPU的offscreen-render
  • drawRect (如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法,因为只要实现了该方法,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以 contentsScale的值,造成资源浪费)
  • 文本(任何类型,包括UILabels,CATextLayers,核心文本等)。
  • 使用Core Graphics
    上面的两种情况使用的就是CPU离屏渲染,首先分配一块内存,然后进行渲染操作生成一份bitmap位图,整个渲染过程会在你的应用中同步的进行,接着再将位图打包发送到iOS里一个单独的进程--render server,理想情况下,render server将内容交给GPU直接显示到屏幕上。
GPU的offscreen-render
  • 设置cornerRadius, masks, shadows,edge antialiasing等
  • 设置layer.shouldRasterize = YES

与硬件(GPU)加速绘图相比,上述CPU处理的任何类型的绘图都很慢,但它们不一定会减慢您的应用程序速度,因为它们不需要每帧都发生。例如,第一次在视图上绘制投影时速度很慢,但在绘制之后它会被缓存,并且只有在视图更改大小或形状时才会重绘。

为了获得良好的性能,诀窍是避免使用软件绘制来改变每一帧的视图。例如,如果您需要动画矢量形状,使用CAShapeLayer或OpenGL比使用drawRect和Core Graphics获得更好的性能。但是如果你画一次形状然后不需要改变它,它就没有太大的区别。

三种观点

  • GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作,这种离屏渲染损耗了性能,因为该过程中开辟空间和上下文切换等损耗性能
  • 导致性能低下的原因并不是因为离屏渲染,因为offscreen 和 onscreen 几乎一样快。重要的是,在渲染的时候是否使用了硬件加速。
  • 切换上下文不是直接导致性能损耗的原因,多做的 Rendering 工作才是。iOS 里讲 CALayer 的一些 properties 会触发「离屏渲染」导致 app 性能下降,其实更多的是指 GPU 要为此多做很多额外的处理步骤(pass),这些多出来的步骤必然消耗更多的设备性能。

个人认为具体情况具体处理,当遇到性能瓶颈的时候需要看是CPU还是GPU造成的性能瓶颈,并进行相应优化

具体的优化方法

  • 圆角

使用CAShapeLayer和UIBezierPath设置圆角

UIImageView *imageView = [[UIImageView >alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; 
imageView.image = [UIImage imageNamed:@"myImg"]; 
UIBezierPath *maskPath = [UIBezierPath >bezierPathWithRoundedRect:imageView.bounds >byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init]; 
//设置大小 
maskLayer.frame = imageView.bounds; 
//设置图形样子 
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer; 
[self.view addSubview:imageView];

view.layer.backgroundColor = [UIColor blueColor].CGColor;
view.layer.cornerRadius = 10;
  • 阴影,使用UIBezierPath绘制
imageView.layer.shadowColor = [UIColor grayColor].CGColor;
imageView.layer.shadowOpacity = 1.0;
imageView.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath = path.CGPath;
  • 先显示UI页面,再异步合并数据到本地
  • 尽量避免使用透明视图,因为使用透明视图的时候GPU需要花费很大工作量做合成计算。图片也尽量不要用有透明通道的图片
  • 当shouldRasterize设成true时,layer被渲染成一个bitmap,并缓存起来,等下次使用时不会再重新去渲染了。实现圆角本身就是在做颜色混合(blending),如果每次页面出来时都blending,消耗太大,这时shouldRasterize = yes,下次就只是简单的从渲染引擎的cache里读取那张bitmap,节约系统资源。
    如果你最后设置了 shouldRasterize 为 YES,那也要记住设置 rasterizationScale 为 contentsScale
    然而,这是一个权衡。第一,这可能会使事情变得更慢。创建额外的屏幕外缓冲区是 GPU 需要多做的一步操作,特殊情况下这个位图可能再也不需要被复用,这便是一个无用功了。然而,可以被复用的位图,GPU 也有可能将它卸载了。所以你需要计算 GPU 的利用率和帧的速率来判断这个位图是否有用。
  • 文本渲染优化
    屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit 或最底层的 CoreText 对文本异步绘制。尽管这实现起来非常麻烦,但其带来的优势也非常大,CoreText 对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整 UILabel 大小时算一遍、UILabel 绘制时内部再算一遍);CoreText 对象占用内存较少,可以缓存下来以备稍后多次渲染。
  • 尽量减少试图层次,必要时可以把多个视图预先渲染为一张图片来显示
  • 其他优化
    https://heisenbean.me/2018/01/iOS-Optimize-Part1/

FlexBox布局

https://juejin.im/post/5a33a6926fb9a045104a8d3c

相关SDK

AsyncDisplayKit

https://www.jianshu.com/p/5c8fb81cf8ab

相关文章

网友评论

      本文标题:保持界面流畅技巧

      本文链接:https://www.haomeiwen.com/subject/fyrvkqtx.html