页面卡顿的优化--圆角

今天产品经理告诉我:诶,那个谁,这个界面很卡诶!!!你看看什么状况。。。因而我掏出了Instrument里的Core Animation看看FPS,发现滑动的时候FPS特别低Orz!html

产生卡顿的缘由

首先,查阅资料看下为何会产生卡顿的缘由。 ios

渲染机制.jpg
在 iOS 系统中,图像内容展现到屏幕的过程须要 CPU 和 GPU 共同参与。CPU 负责计算显示内容,好比视图的建立、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。以后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。因为垂直同步的机制,若是在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留以前的内容不变。这就是界面卡顿的缘由。 那GPU何时会消耗呢,相对于CPU,GPU作的事情比较单一,接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合并渲染,而后输出到屏幕上。宽泛的说,大多数 CALayer 的属性都是用 GPU 来绘制。如下操做会下降GPU的性能:

大量几何结构

全部的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为 GPU Texture。不管是提交到显存的过程,仍是 GPU 调整和渲染 Texture 的过程,都要消耗很多 GPU 资源。当在较短期显示大量图片时(好比 TableView 存在很是多的图片而且快速滑动时),CPU 占用率很低,GPU 占用很是高,界面仍然会掉帧。避免这种状况的方法只能是尽可能减小在短期内大量图片的显示,尽量将多张图片合成为一张进行显示。 另外当图片过大,超过 GPU 的最大纹理尺寸时,图片须要先由 CPU 进行预处理,这对 CPU 和 GPU 都会带来额外的资源消耗。算法

视图的混合

当多个视图(或者说 CALayer)重叠在一块儿显示时,GPU 会首先把他们混合到一块儿。若是视图结构过于复杂,混合的过程也会消耗不少 GPU 资源。为了减轻这种状况的 GPU 消耗,应用应当尽可能减小视图数量和层次,而且减小没必要要的透明视图。布局

离屏渲染

离屏渲染是指图层在被显示以前是在当前屏幕缓冲区之外开辟的一个缓冲区进行渲染操做。 离屏渲染须要屡次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束之后,将离屏缓冲区的渲染结果显示到屏幕上又须要将上下文环境从离屏切换到当前屏幕,而上下文环境的切换是一项高开销的动做。 会形成 offscreen rendering 的缘由有: 阴影(UIView.layer.shadowOffset/shadowRadius/…) 圆角(当 UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一块儿使用时) 图层蒙板 开启光栅化(shouldRasterize = true)性能

圆角优化

因此咱们找到了缘由,因为咱们的项目是基于地图的,在地图上直接添加页面上去,以前地图就有不少圆角设置,加上cell上的圆角,会形成GPU性能的损耗,因此相对来讲页面的滑动会损耗GPU啦。 因而回到了老生常谈的问题了,关于圆角的优化。 以前看文章有说这样去优化:优化

+ (void)cutRadiousWithView:(UIView *)view radious:(CGFloat)radious {
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radious, radious)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
    //设置大小
    maskLayer.frame = view.bounds;
    //设置图形样子
    maskLayer.path = maskPath.CGPath;
    view.layer.mask = maskLayer;
}
复制代码

然而发现并无什么用,其实mask遮罩仍是会发生离屏渲染的。并且亲测这样的FPS貌似更低???(黑人问号脸)spa

图片圆角优化

因而我继续查资料,发现图片的圆角能够将图片进行进行重绘,获得一张新的图片。方法以下:3d

+ (UIImage *)cutCircleImageWithImage:(UIImage *)image size:(CGSize)size radious:(CGFloat)radious {
    UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    CGContextAddPath(UIGraphicsGetCurrentContext(),
                 [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radious].CGPath);
    CGContextClip(UIGraphicsGetCurrentContext());

    [image drawInRect:rect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
复制代码

用这个方法能够获得新的图片,对于图片的圆角处理能够这样作,可是对于View的呢?code

View圆角优化

View的话能够给他盖一层ImageView,设置下Imageview的图片,方法以下:orm

+ (void)cutCicleViewWithView:(UIView *)view radious:(CGFloat)radius {
    CGSize size = view.frame.size;
    CGRect rect = CGRectMake(0, 0, size.width, size.height);
    UIColor *bkColor = view.backgroundColor;

    UIImage *image = [[UIImage alloc] init];
    UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, bkColor.CGColor);
    CGContextAddPath(context,
                 [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
    CGContextDrawPath(context, kCGPathFill);
    [image drawInRect:rect];
    UIImage *output = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:rect];
    imageView.image = output;
    [view insertSubview:imageView atIndex:0];
    view.backgroundColor = [UIColor clearColor];
}
复制代码

总结

相信学过算法的同窗都知道,越快的算法,可能空间复杂度越高。当你为了某一性能去优化,必然会损耗其余的性能,所谓的算法优化是为了达到最佳实现效果。因此这样优化GPU的结果就是会损耗CPU。 过早的优化是魔鬼,可是到问题出来的时候 ,有些东西就有必要去作了!

参考资料:

http://www.reviewcode.cn/article.html?reviewId=7 http://ios.jobbole.com/92237/

相关文章
相关标签/搜索