iOS核心动画高级技巧 总结

读了iOS核心动画高级技巧这本书后,学到并巩固了很多知识。也让原先知道这么用的,但却不明白为什么的,懂得了它的原理。以下是对这本书中的内容的一些问答和总结

  • UIView和CALayer是什么关系,它们之间有什么区别?

    • 关系:
      • UIView仅仅是对它的一个封装,提供了一些iOS类似于处理触摸的具体功能,以及Core Animation底层方法的高级接口;
      • CALayer是UIView内部实现细节,图层才是真正用来在屏幕上显示和做动画
    • 不同点:最大的不同是CALayer不处理用户的交互,CALayer并不清楚具体的响应链(iOS通过视图层级关系用来传送触摸事件的机制),于是它并不能够响应事件。
    • 相同点:同样是一些被层级关系树管理的矩形块,同样也可以包含一些内容(像图片,文本或者背景色),管理子图层的位置
  • bounds 和 frame 有什么区别,它们什么时候一样,什么时候不一样?

    • 区别:
      • frame并不是一个非常清晰的属性,它其实是一个虚拟属性,是根据bounds,position和transform计算而来。
      • bounds 是图层的内部属性,不会因图层的位置改变为改变,只受图层的大小而改变。
    • 在原点且不旋转时是一致的。
  • zPosition 可以用来干什么?如果使用了,需要注意点什么?

    • 可以用来改变显示的层级,zPosition大的在最上层,用户可以看到。
    • 需要注意的是,虽然视觉层次改变了,事件的响应顺序还是按照图层树的顺序来响应的。
  • 判断一个图层是否被点击,你是用什么来做的?是用containsPoint么?如果是,会有什么问题,如果不是,那么你用的是什么?

    • 使用containsPoint需要一层一层的去寻找被点击的图层,每次都需要转换坐标系,多层的情况会比较麻烦。
    • 可以在touch代理中手动调用hitTest:来直接返回一个图层,再判断改图层是否是你想要的,简单而高效。
  • 什么情况下会产生离屏渲染?

    • 圆角或者不规则的角
    • 阴影
    • 蒙板
  • shouldRasterize 一般什么时候会用到?,需要注意什么?

    • 需要进行组配置的时候,比如父视图和所有的子视图设置共同的透明度时。
    • 需要对复杂图层进行扁平化存储时。
    • 需要给rasterizationScale赋值Screen.scale,来消除多倍屏上的像素点。
  • 隐式动画是什么?它和显式动画有什么区别?

    • Layer 层上的一些属性的变化会产生一个默认0.25秒,循环一次的动画为隐式动画。
    • 显示动画的基础也是隐式动画,可以理解为,我们可控的动画为显式动画。即对隐式动画的封装和修改。
  • 怎么禁止隐式动画?

    • 可以通过事务,在事务中setDisableActions:来禁止。这样做的原因是,如果不在事务中进行修改的话,可能会影响到其他地方。事务通过入栈出栈的行为防止了事务中的行为外漏。
    • 我们还可以通过实现图层的actionForLayer:forKey:委托方法,或者提供一个actions字典来控制隐式动画,例如actions方法见代码1:
代码1
CATransition *transition = [CATransition animation];
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
self.colorLayer.actions = @{@"backgroundColor": transition};
//add it to our view
[self.layerView.layer addSublayer:self.colorLayer];
  • Layer 的 presentationLayer 和 modelLayer 有了解过么?分别表示了什么?

    • presentationLayer:实时的layer,对一个正在运动的图层来说,我们需要获取它的点击位置,最好使用该图层。
    • modelLayer:即将到达的图层,图层动画的终点。
  • 图层的speed改变对timeOffset和beginTime有什么影响?

    • speed 会对beginTime 造成影响,例如一个一秒的动画,如果speed(默认1.0)为2.0时,beginTime为0.5时 会直接到动画的末尾,0.5 * 2.0 = 1.0;但是timeOffset 并不会受到影响。
  • 高效绘图

    • 尽量别使用UIView的drawRect:方法和CALayerDelegate中的drawLayer:inContext:方法。如果使用了,图层会创建一个绘制的上下文,这个上下文所占用的内存为:图层宽像素*图层高像素*4字节。例如在iphone 7S 全屏幕进行绘制,所需要的内存为:414 * 3 * 736 * 3 * 4 / 1024 / 1024 = 10.5M 左右的内存,图层每次重绘的时候都需要重新抹掉内存然后重新分配。
    • 如果避免不了绘图,可以使用CAShapeLayer来绘制不规则图形、CAGradientLayer来绘制渐变、CATextLayer来绘制文本等。这避免了图层再次创建一个寄宿图,节省了内存,同时也减少了CPU的负担,因为这些图层使用GPU来硬件加速。
    • 当用特殊图层满足不了需求,使用Core Graphics来进行绘图时,可以使用setNeedsDisplayInRect:来减少对脏矩形的重绘,每次在drawRect:中只重新绘制setNeedsDisplayInRect:传入的区域。
    • GPU每一帧可以绘制的像素有一个最大限制,GPU会放弃绘制那些完全被其他图层遮挡的像素,但是要计算出一个图层是否被遮挡也是相当复杂并且会消耗处理器资源。同样,合并不同图层的透明重叠像素(即混合)消耗的资源也是相当客观的。所以为了加速处理进程,不到必须时刻不要使用透明图层。
    • 另外,适当的使用shouldRasterize可以将一个固定的图层体系折叠成单张图片,这样就不需要每一帧重新合成了,也就不会有因为子图层之间的混合和过度绘制的性能问题了。
  • 动画执行的大概过程

    • 布局 - 这是准备你的视图/图层的层级关系,以及设置图层属性(位置,背景色,边框等等)的阶段。
    • 显示 - 这是图层的寄宿图片被绘制的阶段。绘制有可能涉及你的-drawRect:和-drawLayer:inContext:方法的调用路径。
    • 准备 - 这是Core Animation准备发送动画数据到渲染服务的阶段。这同时也是Core Animation将要执行一些别的事务例如解码动画过程中将要显示的图片的时间点。
    • 提交 - 这是最后的阶段,Core Animation打包所有图层和动画属性,然后通过IPC(内部处理通信)发送到渲染服务进行显示。
    • 之后交给APP外的系统的OpenGL 进行一系列的渲染。
  • GPU

    • 大多数CALayer的属性都是用GPU来绘制
    • GPU 难以处理的部分
      • 过量的几何图形(超过几百万个三角板时)
      • 部分的离屏渲染(有些离屏渲染会发生在GPU)
      • 重绘时,经常发生在多个试图叠加并包含透明度时。
      • 过大的图片,超出2048x2048或者4096x4096时,需要用CPU在图层每次显示之前对图片预处理。
  • CPU

    • 大多数工作在Core Animation的CPU都发生在动画开始之前,所以它不会影响到帧率。
    • 会延迟动画开始的时间的操作
      • 布局,特别是自动布局
      • Core Graphics绘制实现了drawRect:方法,或者CALayerDelegate的drawLayer:inContext:时,因为需要额外的创建一个寄宿图,图越大,消耗的内存越大,CPU也就越难处理,在性能要求高时不要这么做。
      • 解压图片时,在下面的延迟解压中会提到。
  • 延迟解压

    • imageWithContentsOfFile:会延迟解压图片,直到加载到内存中,这就会在准备绘制图片的时候影响性能,因为需要在绘制之前进行解压(通常是消耗时间的问题所在)。
    • 使用imageNamed:会在加载图片之后立即进行解压,一般不会存在问题。
    • 如果不使用imageNamed:(它在内存中自动缓存了解压后的图片,即使你自己没有保留对它的任何引用),那么可以使用把整张图片绘制到CGContext的方式。见代码2。
代码2
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, YES, 0);
[image drawInRect:imageView.bounds];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();                        
  • 图层
    • 初始化图层,处理图层,打包通过IPC发给渲染引擎,转化成OpenGL几何图形,这些是一个图层的大致资源开销
    • 寄宿图可以通过Core Graphics直接绘制,也可以直接载入一个图片文件并赋值给contents属性,或事先绘制一个屏幕之外的CGContext上下文
    • 想要切圆角,不必使用cornerRadius和maskToBounds,因为这会触发离屏渲染,消耗性能。可以使用bezierPathWithRoundedRect:cornerRadius配合CAShapeLayer来实现。或者使用contensCenter。contensCenter相对于CAShapeLayer在规则如圆角时性能差不多,但是绘制不规则边框时contensCenter性能更好。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容