iOS品质优化,UIKit品质分析优化

欲知前事怎样,且看上回分解: iOS质量优化

本文是基于UI基个性能调优实战讲明 - 简书那篇小说的下结论详细请看原稿

经过对品质初级优化秘技一段时间的演习,少侠应该对质量优化有了显明的问询,在平日支付编码中有了些品质优化的意识,当产品小师妹提议两个新的互相的时候,想必也定难不倒少侠了。

实例讲明能够参照:小心别让圆角成了您列表的帧数杀手 - CocoaChina_让运动支付更简短

就列表来讲,icon、大标题、小标题、内容,平时应用软件的众多时候就是那多少个因素,排版分裂,细致效果分歧。那几个对于少侠来说都早已不成问题了,不知不觉中应用软件已经如丝般顺滑。望着产品小师妹那远瞻的眼力,牛春风得意,花前月下,城下之盟登时快要深谋远虑。

fps表示frames per second,也正是每分钟展现多少帧画面。对于静止不改变的内容,大家没有供给思虑它的刷新率,但在试行动画或滑动时,fps的值直接反映出滑动的一唱三叹程度

但,江湖风云突变,折戟沉沙你早有预备。

调治优化:

只是没悟出那一天来的如此快。

1.图层混合:

那一天产品小师妹建议了叁个新的须要,除了以前的icon、大标题、小标题之外,以往要丰盛标签,标签有多少个用于各样活动运行,标签的任务要基于题目内容的职位来定,标签要做成圆角加边框,同时列表每一行的惊人要基于种种内容来最终明确,内容多就高,内容少就矮,还会有icon要圆形加边框顺便带点阴影,巴拉巴拉巴拉巴拉巴拉巴拉。产品小师妹一口气说了无数,说的你头晕,气息杂乱,差一点走火入魔,口吐鲜血,但望着小师妹这依旧的惊喜加梦想的视力,只可以暗暗运力,稳住阵脚,一口答应小师妹的供给。伊人远去,望着小师妹远去的人影,你疯狂编码,但总有那么二个点不能够突破,流畅性始终不能实现须求,不禁陷入了考虑。

第一知道像素:像素正是显示屏上的每二个点由福特ExplorerGB二种颜色组合有的时候候会带有阿尔法,若是某叁个区域覆盖了几个layer,最后的来得效果就还行影响,显色混合会消耗一定的GPU,当把最上层的颜色折射率设置为百分百时GPU就能够忽略上边全体的layer节省不需求的演算

中低端质量优化秘技,只好答复初级的品质优化难题。但这几天的须要,效果多,子视图多,排版更新往往,中度每行不平等。初级秘技已经不能够很好的凑效,那可如何是好。

先是个调弄整理选项"Color Blended Layers"就是用于检查测量试验何地发生了图层混合,并用深藕红标记出来。由此大家要求尽或然裁减见到的新民主主义革命区域。一旦挖掘应该设法设法消除它

少侠莫慌,老夫看你已经熟习了初级质量优化法门,基础已经打牢,未来就将品质优化中级秘诀传授与您罢。

消除方案:a.将控件的opaque = true 原理是意在幸免图层混合,不过意义相当的小,因为UIView的那脾天性暗中认可正是true只要不是人为的设置为透明都不会冒出图层混合,但对此UIImageView他本身和图纸都不能为透明的,图片本人的性子也只怕会对结果爆发潜移默化,比opaque属性更重视的是backgroundColor属性,如若不设置那么些性情,控件依旧被感觉是透明的,所以大家做的是将他设置为深红

工欲善其事必先利其器,想要击败对手,你要有趁手的枪炮。

PS:尽管label文字有汉语,依然相会世图层混合,那是因为这时label多了一个sublayer,若是有好的解决办法迎接告诉笔者。

在应用软件里间接的洞察看FPS数据:

2.光栅化光栅化会产生离屏渲染

KMCGeigerCounter也能够依赖 CADisplayLink 自身写一个轻巧易行好用的,CADisplayLink 是二个沙漏,并且那几个电磁照望计时器的调用频率跟显示屏刷新频率一样。

光栅化是将三个layer预先渲染成位图,然后参与缓存中。假使对于影子效果这样相比消耗财富的静态内容开展缓存,能够博得一定幅度的习性进步。demo中的这一行代码表示将label的layer光栅化:

一流法宝,当属 Instrument:

label.layer.shouldRasterize = true

若想熟知使用此项法宝,需注意三个地点

Instrument中,第2个调护治疗选项是“Color Hits Green and Misses Red”,它意味着只要命中缓存则呈现为银白,否则显示为石绿,分明深黑越来越多越好,煤黑越少越好。

  1. 用release格局,贴近最真是的行使情况,本事博取最可信的数量。
  2. 用真机测量检验,模拟器再决定也照旧在模仿,祭出分裂型号的真机,才干针对优化。

光栅化的缓存机制是一把双刃剑,先写入缓存再读取有极大大概损耗比较多的时日。因而光栅化仅适用于较复杂的、静态的效应。通过Instrument的调试发掘,这里运用光栅化常常出现未命中缓存的情况,如果没有新鲜必要则能够关闭光栅化

展开药形式: Xcode -> Product -> Profile -> Core Animation 合营TimeProfile 一齐利用查看FPS的同不常候,还能够查见到何以操作比较耗费时间,有此傍身,再决定的仇敌也会表露缺欠。

3.颜色格式:

图片 1查看FPS

像素在内部存款和储蓄器中的布局和它在磁盘中的存款和储蓄格局并不相同。思考一种轻易的气象:每个像素有PRADO、G、B和阿尔法八个值,各样值占用1字节,因而各类像素占用4字节的内部存款和储蓄器空间。一张一九二〇*1080的相片(小米6 Plus的分辨率)一共有2,073,600个像素,因而占领了当先8Mb的内存。然而一张同样分辨率的PNG格式或JPEG格式的图片平时景色下不会有那样大。那是因为JPEG将像素数据实行了一种特别复杂且可逆的中转。

性情优化的步调:修改 -> Instrument查看 -> 修改 -> Instrument查看 —> 修改.....重复以上动作直到品质达到供给

诸如利用中有一部分从网络下载的图形,而GPU恰好不帮助这一个格式,那就须求CPU预先进行格式转化

CPU的耗费时间操作能够在Instrument里查看见,并定位修改优化,但GPU的优化要怎么开展呢?

其多个挑选“Color Copied Images”就用来检查实验这种实时的格式转化,借使有则会将图纸标识为巴黎绿。

XCode9自此可以Xcode -> Debug > View Debugging > Rendering 下看见优化的次第选项,模拟器时不可能勾选,只有真机的图景下手艺勾选。

4.图片大小

图片 2翻看优化增选

第两个挑选“Color Misaligned Images”。它代表一旦图片要求缩放则标识为豆沙色,若无像素对齐则标识为石榴红

  • Color Blended Layers — 出现图层混合的地点会注解为乳白,未有图层混合的地点会来得为朱红,方向是丙申革命越少越好,藤黄越来越多越好。

其八个优化是调动具备图片的像素大小以免止不须要的缩放。

图片 3图层混合

5.离屏渲染

  • Color Hits Green and Misses Red — 当使用光栅化渲染(shouldRasterize)的时候,假使图层是粉色,表示那一个缓存被复用,如若图层是革命表示缓存未有被复用会另行创立,那时候会招致质量难点。

常规的渲染通道:OpenGL提交八个指令到Command Buffer,随后GPU伊始渲染,渲染结果放到Render Buffer中,那是常规的渲染流程

图片 4光栅化

有部分复杂的功力不可能直接渲染出结果,它必要分步渲染最终再结合起来,例如增添一个蒙版:GPU分别赢得了纹路(texture,也正是十三分相机Logo)和layer的渲染结果。但这四个渲染结果未有间采用入Render Buffer中,也就象征这是离屏渲染。直到第四个渲染通道,才把互相结合起来归入Render Buffer中。离屏渲染意味着把渲染结果不时保存,等用到时再抽取,由此相对于经常渲染更占用能源。

  • Color Copied Images — 假使GPU不支持当前图片格式,那么图片会付给CPU实行事先管理,那张图片会展现为黄铜色。

  • Color Misaligned Images — 质量评定图片是不是被拉伸,当图片色实际尺寸跟ImageView的大小不平等时,就能够时有发生,呈现为深红,这种操作会相比消耗CPU能源。

  • Color Offscreen-rendered Yellow — GPU的渲染有三种,On-screen Rendering当前显示屏渲染,是指GPU的渲染在时下显示屏的缓冲区内展开。off-screen Rendering是指在GPU的渲染发生在现阶段显示屏之外新开垦的缓冲区。开垦新的缓冲区,切换缓冲区等会对质量有异常的大的震慑。

第七个接纳“Color Offscreen-Rendered Yellow”会把须求离屏渲染的地点标识为青灰,半数以上景况下大家需求尽或许幸免暗红的产出。离屏渲染大概会自行触发,也足以手动触发。以下情状可能会变成触发离屏渲染:

触发离屏渲染有以下二种表现:

重写drawRect方法

  1. cornerRadius以及masksToBounds同不常候选择时会触发离屏渲染,单独选取时不会触发。
  2. 设置shadow,并且shodowPath = nil时会触发。
  3. mask 设置蒙版会触发。
  4. layer.shouldRasterize的不相符选拔会触发离屏渲染。
  5. layer.allowsGroupOpacity iOS7自此私下认可开启;当layer.opacity != 1.0且有subLayer也许背景图时会触发。
  6. layer.allowsEdgeAntialiasing 在iOS8今后的系列里恐怕曾经做了优化,并不会触发离屏渲染,不会对质量形成影响。
  7. 重写了drawRect。

有mask或许是影子(layer.masksToBounds, layer.shadow*),模糊效果也是一种mask

少侠熟读了上述招式,便能便捷寻觅敌手的破碎。

layer.shouldRasterize = true

寻找了敌人的破损,少侠还要拟订详细的对答战术,瞅准机缘,方能一招克敌。

前两个会自动触发离屏渲染,第三种情势是手动开启离屏渲染。

老夫那就给您展现克服仇人之道:

第1个优化,在装置阴影效果的四行代码上面增加一行:

  • Color Blended Layers:

    • UIView的backgroundColor不要设置为clearColor,最佳设置的和superView的backgroundColor颜色一样。
    • 图形防止使用带阿尔法通道的图片,无论是地点图片照旧后台重临图片。什么,设计妹子不允许,少侠那将要靠你的吸重力啦。
  • Color Hits Green and Misses Red: 在低档品质优化中,适当使用shouldRasterize中有详实批注。

  • Color Copied Images: 开采进程中注意图片格式

  • Color Misaligned Images: 尽量把图片大小设置的和UIImageView同样大小。

  • Color Offscreen-rendered Yellow: 那是性质优化的要领,针对引起离屏渲染的各类意况需求各样应对

imgView.layer.shadowPath = UIBezierPath(rect: imgView.bounds).CGPath

重大来了,离屏渲染的优化招式,少侠看稳重了

那行代码制订了影子路线,如果未有手动内定,Core Animation会去自动计算,这就能够触发离屏渲染。倘若人为钦定了阴影路线,就足避防去总括,进而制止发生离屏渲染。

  • 设置圆角cornerRadius:

安装cornerRadius本身并不会促成离屏渲染,但为数不菲时候它还亟需卓殊layer.masksToBounds

true使用。根据从前的总计,设置masksToBounds会招致离屏渲染。技术方案是硬着头皮在滑行时防止设置圆角,假设非得设置圆角,能够选用光栅化本事将圆角缓存起来:

// 设置圆角 ps:用后用后无意义

label.layer.masksToBounds = true

label.layer.cornerRadius = 8

label.layer.shouldRasterize = true

label.layer.rasterizationScale = layer.contentsScale

6.高效路线:

此前将离屏渲染和渲染路线时的表示图么,离屏渲染的终极一步是把原先的七个渠道组合起来。假若那么些组合进程能由CPU达成,就能够大方减去GPU的劳作。这种才具在绘制地图中恐怕用到。

第七个选项“Color Compositing 法斯特-Path Blue”用于标识由硬件绘制的不二诀要,紫灰更多越好。

7.变通区域

刷新视图时,大家应该把须求重绘的区域尽量裁减。对于未发生变化的内容则不应该重绘,第三个选择“Flash updated Regions”用于标识产生重绘的区域。一个规范的事例是系统的机械时钟应用,绝大许多时候独有显示秒针的区域需求重绘:

总结

纵然你一步一步成功了那边,小编想一定会有成都百货上千低收入。可是,学而不思则罔,思而不学生守则殆。入手推行后依然应当总括提炼,优化滑动品质首要涉及七个地点:

幸免图层混合

保证控件的opaque属性设置为true,确定保证backgroundColor和父视图颜色同样且不透明

如无特需,不要设置低于1的阿尔法值

确保UIImage没有alpha通道

防止有时转移

管教图片大小和frame一致,不要在滑行时缩放图片

确定保证图片颜色格式被GPU援助,防止劳烦CPU转变

慎用离屏渲染

大多时候离屏渲染会默转潜移属性

重写drawRect方法,设置圆角、阴影、模糊效果,光栅化都会促成离屏渲染

安装阴影效果是加多阴影路线

滑动时若要求圆角功效,开启光栅化

实战

本文的demo能够在自己的Github上下载,然后一步一步自身经验优化进度。但demo终究是特意搭建的四个情况,我会在自己要好的仿写的简书app上连发拓宽实战优化,应接共同学习沟通。

UIView圆角:

#import "UIView+AddCorner.h"

@implementation UIView (AddCorner)

-roundbyuint: num anUint:unit{

double remain = modf(num, &unit);

if (remain > unit / 2.0) {

return [self ceilbyuint:num andUint:unit];

} else {

return [self floorbyuint:num andUint:unit];

}

}

-ceilbyuint:num andUint:unit{

return num - modf(num, &unit) + unit;

//将浮点数num分解成整数部分和小数部分

}

-floorbyuint:num andUint:unit{

return num - modf(num, &unit) ;

}

-piexl:num{

double unit;

switch [UIScreen mainScreen].scale) {

case 1: unit = 1.0/1.0; break;

case 2: unit = 1.0/2.0; break;

case 3: unit = 1.0/3.0; break;

default: unit = 0.0;

break;

}

return [self roundbyuint:num anUint:unit];

}

-kt_viewAddCorner:radius andBorderWidth:borderWidth andBorderColor:borderColor andBackgroundColor:backgroundColor{

UIImageView * imageView = [[UIImageView alloc]initWithImage:[self ViewAddCorner:radius andBorderWidth:borderWidth andBorderColor:borderColor andBackgroundColor:backgroundColor]] ;

[self insertSubview:imageView atIndex:0];

}

-(UIImage *)ViewAddCorner:radius andBorderWidth:borderWidth andBorderColor:borderColor andBackgroundColor:backgroundColor {

CGSize sizeToFit = CGSizeMakeCGRectGetWidth(self.frame), CGRectGetHeight(self.frame));

CGFloat halfBorderWidth = borderWidth / 2.0;

UIGraphicsBeginImageContextWithOptions(sizeToFit, false, [UIScreen mainScreen].scale);

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetLineWidth(context, borderWidth);

CGContextSetStrokeColorWithColor(context, borderColor.CGColor);

CGContextSetFillColorWithColor(context, backgroundColor.CGColor);

CGFloat width = sizeToFit.width, height = sizeToFit.height;

CGContextMoveToPoint(context, width - halfBorderWidth, radius + halfBorderWidth); // 起先坐标左侧开首

CGContextAddArcToPoint(context, width - halfBorderWidth, height - halfBorderWidth, width - radius - halfBorderWidth, height - halfBorderWidth, radius); // 右下角角度

CGContextAddArcToPoint(context, halfBorderWidth, height - halfBorderWidth, halfBorderWidth, height - radius - halfBorderWidth, radius); // 左下角角度

CGContextAddArcToPoint(context, halfBorderWidth, halfBorderWidth, width

  • halfBorderWidth, halfBorderWidth, radius); // 左上角

CGContextAddArcToPoint(context, width - halfBorderWidth, halfBorderWidth, width - halfBorderWidth, radius + halfBorderWidth, radius) ;// 右上角

CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);

UIImage *output = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return output;

}

@end

UIImageView圆角:

#import "UIImageView+AddCorner.h"

@implementation UIImageView (AddCorner)

-kt_ImageViewAddCornerWithRadius:radius{

// self.image = self.image?:[self.image imageAddCornerWithRadius:radius andSize:self.bounds.size];

//注意第多少个选项的设置

UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [UIScreen mainScreen].scale);

//在绘制此前先裁剪出二个圆形

[[UIBezierPath bezierPathWithRoundedRect:self.bounds

cornerRadius:radius] addClip];

//图片在设置的圈子里面举办绘图

[self.image drawInRect:self.bounds];

//获取图片

self.image = UIGraphicsGetImageFromCurrentImageContext();

//截止绘制

UIGraphicsEndImageContext();

}

@end

UIImage圆角:

#import "UIImage+ImageRoundedCorner.h"

@implementation UIImage (ImageRoundedCorner)

- imageAddCornerWithRadius:radius andSize:size{

CGRect rect = CGRectMake(0, 0, size.width, size.height);

UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);

CGContextRef ctx = UIGraphicsGetCurrentContext();

UIBezierPath * path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];

CGContextAddPath(ctx,path.CGPath);

CGContextClip;

[self drawInRect:rect];

CGContextDrawPath(ctx, kCGPathFillStroke);

UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return newImage;

}

@end

UIView: 假诺view.layer.contents 为空,直接通过设置view.layer.cornerRadius 以及 view.backgroundColor大概view.layer.border就可以设置圆角,无需安装masksToBounds为YES,此时不会发出离屏渲染。

//设置圆角边框view.layer.cornerRadius = 3.0view.layer.borderColor = UIColor.red.cgColorview.layer.borderWidth = 1.0// 设置带背景view.layer.cornerRadius = 3.0view.backgroundColor = UIColor.green//或者相同效果的view.layer.backgroundColor = UIColor.green.cgColor

UILabel: 设置和UIView差不离,有一个区分就是设置label.backgroundColor和layer.cornerRadius不会起功效,须求设置label.layer.backgroundColor和layer.cornerRadius才会起作用。

//设置圆角边框view.layer.cornerRadius = 3.0view.layer.borderColor = UIColor.red.cgColorview.layer.borderWidth = 1.0

UITextField: 自带圆角作用,设置差别style就能够直达效果。

UITextView: 和UIView的安装形式一致。

UIImageView:

UIImageView的情景比较独特,上边的三种方法无法兑现圆角,应当要layer.cornerRadius和layer.masksToBounds

YES,才干达成圆角。但以此操作必定会发生离屏渲染,为了制止离屏渲染,常用的优化措施有:

  • 重绘图片,生成一张带圆角的图片,然后设置到UIImageView上。

    func redrawImage(originImage: UIImage, rectSize: CGSize, cornerRadius: CGFloat) -> UIImage? { UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale) if let context = UIGraphicsGetCurrentContext() { let rect = CGRect(origin: CGPoint.zero, size: rectSize) let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)) context.addPath(path.cgPath) context.clip() originImage.draw context.drawPath(using: .fillStroke) let roundedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return roundedImage } return nil } DispatchQueue.global(qos: .default).async { //在子线程调用redrawImage生成图片 DispatchQueue.main.async { //在主线程设置图片 } }
    
  • 在UIImageView上覆盖一张部分透明的,部分遮挡的图纸,盖在本来的UIImageView上,曲线达成图片圆角功用。

    //生成中间透明 周围遮挡的图片func getRundedCornerImage(radius: CGFloat, rectSize: CGSize, fillColor: UIColor) -> UIImage? { UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale) if let currentContext = UIGraphicsGetCurrentContext() { let rect = CGRect(origin: .zero, size: rectSize) let outerPath = UIBezierPath(rect: rect) let innerPath = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)) currentContext.setBlendMode fillColor.setFill() outerPath.fill() currentContext.setBlendMode innerPath.fill() let roundedCornerImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return roundedCornerImage } return nil} //将生成的图片 加到需要圆角的图片的上方
    
  • 设置阴影shadow:

    设置shadowPath,能够缓和离屏渲染难题。

     self.shadowView.layer.shadowColor = UIColor.gray.cgColor self.shadowView.layer.shadowOpacity = 0.2 self.shadowView.layer.shadowRadius = 3.0 self.shadowView.layer.shadowOffset = CGSize(width: 1, height: 1) self.shadowView.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
    

    本来和圆角的消除办法一样,能够运用一张带阴影的图来曲线解决难点。

    func getRundedCornerShadowImage(originImage: UIImage, rectSize: CGSize, roundedRadius: CGFloat, shadowColor: UIColor, shadowOffset: CGSize, insetX: CGFloat, insetY: CGFloat) -> UIImage? { UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale) if let currentContext = UIGraphicsGetCurrentContext() { let rect = CGRect(origin: .zero, size: rectSize) let shadowPath = UIBezierPath(roundedRect: rect.insetBy(dx: insetX, dy: insetY), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: roundedRadius, height: roundedRadius)) currentContext.setShadow(offset: shadowOffset, blur: roundedRadius, color: shadowColor.cgColor) currentContext.addPath(shadowPath.cgPath) shadowPath.fill() let imagePath = UIBezierPath(roundedRect: rect.insetBy(dx: insetX, dy: insetY), byRoundingCorners: .allCorners, cornerRadii: CGSize(width: roundedRadius, height: roundedRadius)) currentContext.addPath(imagePath.cgPath) currentContext.clip() originImage.draw(in: rect.insetBy(dx: insetX, dy: insetY)) currentContext.strokePath() let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return image } return nil} 
    
  • 设置蒙版mask:

    安装mask必定会触发离屏渲染。mask的经过差相当的少来看是和视图混合相反的长河,举个例子有一张图片,中间有三个圆形空间是晶莹的,边缘部分是大青,假诺视图直接叠合在一张头像上,会显示出圆形头型的功能,但万一采取mask则会展现出中间白边缘透明的效果。所以品质敏感的分界面中,能够不应用mask,而使用视图混合这种对品质影响越来越小的章程展开操作。

  • layer.allowsGroupOpacity、layer.allowsEdgeAntialiasing:

    那五个操作对质量并不会导致异常的大的影响。

  • drawRect:

    drawRect会促成十分的大的内部存款和储蓄器消耗,并会导致离屏渲染,应尽量防止重写。

以上招式,少侠可主见了,日后定当优异演习,得到伊人芳心指日可待。

快去找小师妹去罢。

本文由365bet体育在线官网发布于网络编程,转载请注明出处:iOS品质优化,UIKit品质分析优化

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。