iOS核心动画高级技巧:第一部分-图层

本文是要点型笔记,建议先阅读以下参考内容后再阅读,完成知识体系的构建:
 [1] View Programming Guide for iOS
 [2] Core Animation Programming Guide
 [3] ios核心动画高级技巧
 [4] 通过实现一个TableView来理解IOS UI编程
 [5] 详解 CALayer 和 UIView 的区别和联系
 [6] iOS绘图教程
 [7] Quartz 2D编程指南之一~十三 - 南峰子


LayerKit => Core Animation是一个复合引擎,它的职责就是尽可能快地组合屏幕上不同的可视内容,可视内容(图片、文本、背景色、…)被分解成独立的图层,存储在一个叫做【图层树】的体系之中。

图层可以通过【寄宿图】来提供可视内容,通过【图层几何学/布局特性】锁定摆放的位置,可以加一些基础【视觉效果】(圆角、边框、阴影、蒙版、拉伸、透明…)或高级视觉效果-【变换】(仿射、3D);除了图片和背景纯色之外,CA还提供了一系列【专有图层】对绘制能力进行了扩展,针对特殊场景进行了封装和性能优化。

注:优先级-displayLayer: > -drawInContext: > -drawLayer:inContext: > -drawRect:

附 UIKit & Quarts 2D:


第一章:图层的树状结构

1. 四个平行的层级关系

视图树 —— UIView,着重于交互-响应事件(触摸/手势…),作为CALayer的管理者
模型树/逻辑树(目标帧) —— CALayer - modelLayer(),不涉及响应链(通过视图层级关系传递触摸事件的机制)
呈现树/动画树(当前帧) —— presentationLayer(), 动画过程中的当前值,详见第七章
渲染树/显示树(下一帧) —— 图层和动画打包提交到渲染服务后反序列化所得树,被用于生成gl三角形s,详见第十二章

2. CALayer和UIView的区别和联系

  • 两者通过UIView的layerClass类方法layer属性联系在一起
  • 属性封装 - Layer 的 frame 是由 anchorPoint,position,bounds和 transform 共同决定的,而View 只是简单的返回 Layer的 frame(bounds和center/position类似)
  • 分工 - UIView侧重对显示内容的管理,而CALayer侧重显示内容的绘制(drawXxx())和显示(display())
  • 事件响应 - UIResponder决定了UIView能接受并响应事件,而CALayer不可以(即使有Hit Testing方法)
  • 隐式动画 - 非根CALayer 支持隐式动画,而UIView 作为 Layer 的默认代理,通过在actionForLayer:forKey:返回nil默认禁止了隐式动画(在animation block中会自动重新启用,本质:[CATransaction setDisableActions:NO];)
  • 视图未暴露的CALayer的功能:
    • 锚点、阴影,圆角,边框、透明遮罩
    • 非矩形范围(mask or shadowPath???)
    • 3D变换(UIView的transform; CALayer的transformaffineTransform
    • 多级非线性动画(UIView的默认显式动画都是线性动画)

3. CALayer和UIView的选择

UIView的适用场景

  • 优先使用,原因:自动排版/布局和事件处理

CALayer的适用场景:

  • 可以在Mac OS上运行的跨平台应用
  • 使用多种CALayer的子类(见第六章),并且不想创建额外的UIView去包封装它们
  • 对性能要求特别高 => 考虑用CALayer或OpenGL绘图

第二章:设置寄宿图 - 给图层提供内容


第三章:图层几何学

  • 布局属性 - 在图层级别无法根据兄弟图层控制位置和尺寸
    • frame(外 - 父图层坐标系)
    • bounds(内 - 自身坐标系)
    • anchorPoint - 锚点在自身坐标系中的(x,y) - 最佳实例:时钟应用中的指针转动
    • center(UIView)/position(CALayer) - ️锚点在父图层坐标系中的(x,y)

第四章:视觉效果

  • 圆角 - conrnerRadius
    • 只影响背景颜色而不影响背景图片或是子图层
    • 通常结合masksToBounds使用,来裁剪子图层和背景图片
  • 图层边框 - borderWidth和borderColor
  • 边框是绘制在图层边界里面的,而且在所有子内容之前,也在子图层之前。
  • 实现边框不遮挡内容的两种做法
    • 使用带边框效果的寄宿图
    • 让边框和内容解除父子关系,并让内容在图层树中靠前
  • 阴影 - shadowOpacityshadowColorshadowOffset(方向和距离)和shadowRadius(模糊度)

    • 与边框不同
      • 图层的阴影继承自内容(包含寄宿图和子视图)的外形,而不是根据边界和角半径来确定。
      • 阴影可能在图层边界之外;会被masksToBounds裁剪掉
      • 解决方案:使用两个图层(一个只画阴影的空的外图层和一个用masksToBounds裁剪内容的内图层)
    • shadowPath - 提高性能,CGPathRef类型
  • 图层蒙板

    • mask - 类似于一个子图层,但它定义的是父图层的部分可见区域
    • 比conrnerRadius+masksToBounds更灵活:蒙板图不局限于静态图,以通过编码甚至动画的方式实时生成,而且让子图层或子视图裁剪成同样的形状
  • 拉伸过滤

  • 较小的图或者是差异特别明显、极少斜线的大图,Nearest会保留这种差异明显的特质以呈现更好的结果
  • 对于大多数的图尤其是有很多斜线或曲线轮廓的图片,选用kCAFilterLinear或kCAFilterTrilinear
  • 组透明
    • alpha(UIView)、opacity(CALayer)都会影响子层级
      组透明设置造成的像素合成
    • 组透明整体实现方案(解决上述透明度混合造成显示不一致的问题)
      • UIViewGroupOpacity -> YES(Info.plist)
      • allowsGroupOpacity(IOS7+可用,相当于UIViewGroupOpacity)
      • shouldRasterize+rasterizationScale(配置屏幕),如果它被设置为YES,在应用透明度之前,图层及其子图层都会被整合成一个整体的图片

第五章:变换

  • CGAffineTransform
    • “仿射”:图层中平行的两条线在变换之后仍然保持平行
    • 混合变换 - 变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的旋转结果可能不同
  • CATransform3D
    • 透视投影 - m34 = -1.0/d,d代表了假想中视角相机和屏幕之间的距离,通常设置为500~1000
    • 灭点 - anchorPointZ+anchorPoint,旋转和缩放的不变点 => sublayerTransform
    • 背面 - doubleSided属性,区别:doubleSided不影响事件响应,设置hidden或alpha为0不会响应事件
  • 固体对象
    • 光亮和阴影 - GLKMatrix3Xxx和GLKVector3Xxx方法
    • 如何让图层顺序靠前(响应链中靠后)的图层得到事件响应
      • bringSubviewToFront、sendSubviewToBack、exchangeSubviewAtIndex(_:withSubviewAtIndex:)等方法可以同时改变显示顺序和图层顺序(响应链优先级)
      • 通过将优先级更高的UIView的userInteractionEnabled设置为NO

第六章:专用图层 +(Class)layerClass

  • CAShapeLayer - 通过矢量图形而不是bitmap来绘制寄宿图的图层子类
    • 相比在CALayer中绘制路径而言优势如下:
      • 渲染快速(硬件加速)
      • 高效使用内存(不需要寄宿图)
      • 不会被图层边界剪裁掉(可以在边界之外绘制)
      • 不会出现像素化(因为是矢量图形)
    • 属性
      • fillColor、strokeColor、lineWidth(单位:点)、lineCap、lineJoin
      • path - UIBezierPath.CGPath
    • 圆角 - 相比使用cornerRadius更灵活
  • CATextLayer - 使用了Core text,渲染非常快
    • 属性
      • string - (富文本使用NSAttributedString)
      • font 和 fontSize、wrapped(自动换行)、truncationMode(截断模式)、alignmentMode
    • 文本像素化
textLayer.contentsScale = UIScreen.mainScreen().scale
  • UILabel替代品 - 用CATextLayer作为宿主图层的UILabel子类,可以随着视图自动调整大小而且也没有冗余的寄宿图
  • CATransformLayer

    • 不能显示自己的内容,只有当存在了一个能作用于子图层的变换它才真正存在,而且变换将对所有子图层的3D场景生效而不是扁平化后的子图层。
    • CATransformLayer并不平面化它的子图层,所以它能够用于构造一个层级的3D结构
  • CAGradientLayer

    • 用来生成两种或更多颜色平滑渐变,绘制使用了硬件加速
    • 基础渐变
      • startPoint和endPoint
      • colors(CGColorRef数组)
    • 多重渐变
      • locations属性
      • 以NSNumber包装的浮点数值数组,定义了colors属性中每个不同颜色的位置(以单位坐标系标定)
      • 确保数组大小和colors数组大小一定要相同
  • CAReplicatorLayer

    • 高效生成许多相似的图层,并在每个复制体上应用不同的变换。
    • 属性
      • instanceCount
      • instanceTransform
      • instanceRed/Green/BlueOffset、instanceAlphaOffset
      • ……
    • 反射 - ReflectionView
  • CAScrollLayer

    • -scrollToPoint:方法,自动适应bounds的原点以便图层内容出现在滑动的地方
    • 并不负责将触摸事件转换为滑动事件,既不渲染滚动条,也不实现任何iOS指定行为例如滑动反弹(当视图滑动超多了它的边界的将会反弹回正确的地方)。
    • UIScrollView并没有用CAScrollLayer,事实上,就是简单的通过直接操作图层边界来实现滑动。
var visibleRect: CGRect { get }
func scrollPoint(_ p: CGPoint)  // 从图层树中查找并找到第一个可用的CAScrollLayer,然后滑动它使得指定点成为可视的
func scrollToRect(_ r: CGRect)  // 类似,不过是使指定矩形可视
  • CATiledLayer
    • OpenGL有一个最大的纹理尺寸(通常是20482048或40964096,这个取决于设备型号),如果你想在单个纹理中显示一个比这大的图,即便图片已经存在于内存中了,你仍然会遇到很大的性能问题。所以需要使用类似CATiledLayer的技术
-(void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx //在此根据需要加载不同的tile
class func fadeDuration() -> CFTimeInterval
tileLayer.contentsScale = [UIScreen mainScreen].scale;

CAEmitterLayer看上去像是许多CAEmitterCell的容器,这些CAEmitierCell定义了一个例子效果。你将会为不同的例子效果定义一个或多个CAEmitterCell作为模版,同时CAEmitterLayer负责基于这些模版实例化一个粒子流。一个CAEmitterCell类似于一个CALayer:它有一个contents属性可以定义为一个CGImage,另外还有一些可设置属性控制着表现和行为。

- 一个高性能的粒子引擎,被用来创建实时粒子动画如:烟雾,火,雨等等这些效果。
- 自身属性
  + emitterCells
  + preservesDepth
  + renderMode
    - kCAEmitterLayerAdditive是叠加效果,其他是覆盖效果
- [CAEmitterCell](https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CAEmitterCell_class/)的属性
 + contents、color 
 + birthRate
 + lifetime 、lifetimeRange - 类似velocity
 + alphaSpeed 
 + velocity、velocityRange - [velocity~(velocity+velocityRange)]定义了速度变化范围
 + emissionLatitude(z轴)、emissionLongitude(xy平面)、emissionRange(环绕发射角度的一个圆锥范围)
  • CAEAGLLayer
    • OpenGL没有对象或是图层的继承概念,它的全部工作就是处理3D空间中有颜色和纹理的三角形
    • GLKView(GLKit框架) + CAEAGLLayer(QuartzCore)
    • 重要元素
class func setCurrentContext(_ context: EAGLContext!) -> Bool
func renderbufferStorage(_ target: Int, fromDrawable drawable: EAGLDrawable!) -> Bool
func presentRenderbuffer(_ target: Int) -> Bool
 + CAEAGLLayer的[drawableProperties属性](https://developer.apple.com/library/ios/documentation/iPhone/Reference/EAGLDrawable_Ref/index.html#//apple_ref/occ/intfp/EAGLDrawable/drawableProperties)(EAGLDrawable协议)
 + [GLKBaseEffect](https://developer.apple.com/library/ios/documentation/GLkit/Reference/GLKBaseEffect_ClassRef/) - 着色逻辑:func prepareToDraw()
 + glXxx系列方法(setupBuffers & drawFrame)
  • AVPlayerLayer
    • 由AVFoundation框架提供 (不同于其他CALayer由Core Animation框架提供)
    • 播放控制和展现分离:AVPlayer和AVPlayerLayer
      • AVPlayer - play()、pause()、rate等
      • AVPlayerLayer - player属性和init(player player: AVPlayer)
        • 继承了父类CALayer的所有特性,不会受限于要在一个矩形中播放视频;
        • 可实现3D、圆角、有色边框、蒙板、阴影等效果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容