前言

咱们在前面,首要进行了针对 iOS中的多媒体技能相关几个结构概述:

  1. 然后 用 两篇文章 对 其间的 UIKit相关关键 进行了分述:
  2. 咱们 在此篇文章 ,将 针对 Core Animation结构的关键 进一步打开分述:

一、Core Animation简介

1. 官方介绍

依照惯例,咱们首要引进苹果官方文档对其的介绍:

Render, compose, and animate visual elements.
OverView

Core Animation provides high frame rates and smooth animations without burdening the CPU or slowing down your app. Core Animation does most of the work of drawing each frame of an animation for you. You’re responsible for configuring the animation parameters, such as the start and end points, and Core Animation does the rest. It accelerates the rendering by handing over most of the work to dedicated graphics hardware. For more details, seeCore Animation Programming Guide.

  • Core Animation 供给高帧速率和流畅的动画,不会增加 CPU 担负或下降运用程序速度。
  • 中心动画为您完结制造动画每一帧的大部分作业
    • 开发者只需求担任装备动画参数,例如起点结尾,中心动画会完结其他的作业。
    • 它经过将大部分作业移交给专用图形硬件来加快烘托。
    • 有关更多具体信息,请参阅 Core Animation Programming Guide

Core Animation中心关键

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

咱们翻开 中心动画的 编程指南 Core Animation Programming Guide,咱们能够看到,关于 Core Animation的介绍有许多章节,他们能够扼要概括为:

  • Core Animation 是 iOS 和 OS X 上都可用的图形烘托动画根底结构
  • Core Animation 的中心目标是 layer目标 (CAlayer以及它的派生类)

2. 结合项目实战了解

我在 探究 iOS图层烘托原理 的时分也对Core Animation做过注解:

  • Core Animation,它本质上能够了解为一个复合引擎首要责任包含:烘托、构建和完结动画
  • 一般咱们会运用 Core Animation 来高效、便利地完结动画,可是实践上它的前身叫做Layer Kit,关于动画完结仅仅它功用中的一部分
  • 关于 iOS app,不论是否直接运用了 Core Animation,它都在底层深度参加了 app 的构建
  • Core Animation 是 AppKit 和 UIKit 完美的底层支撑,同时也被整合进入 Cocoa 和 Cocoa Touch 的作业流之中,它是 app 界面烘托和构建的最根底架构
  • Core Animation 的责任便是尽或许快地组合屏幕上不同的可视内容,这个内容是被分解成独立的 layer(iOS 中具体而言便是 CALayer),而且被存储为树状层级结构。
  • 这个树也形成了 UIKit 以及在 iOS 运用程序傍边咱们所能在屏幕上看见的一切的根底。

接下来咱们就环绕 layer目标图形烘托动画 这三个关键,逐步打开对 Core Animation 结构 的回忆

二、中心目标layer

  • Core Animation 自身并不是绘图体系
    它是用于在硬件中组成和操作运用程序内容的根底设施。
  • Core Animation 作为图形烘托动画的根底设施,其间心是 layer 目标
    • 开发者能够运用layer来办理和操作内容。
    • layer将您的内容捕获到能够由图形硬件轻松操作的位图中。
    • 在大多数运用程序中,layer用作办理视图内容的办法
    • 开发者也能够依据需求创立独立图层。
    • 修正layer中的特色触发动画
      • 与View相同,图层目标也有一个鸿沟矩形、屏幕上的方位不通明度transform以及许多其他能够修正的面向视觉的特色。
      • 更改特色值会导致创立隐式动画
    • layer 能够像 View相同 有多个层级

1. layer目标简介

  • layer目标是在 3D 空间中安排的 2D 外表,是运用 Core Animation 所做的一切的中心。
  • 与View相似:
    • layer办理有关其外表的几许形状内容视觉特色的信息。
  • 与View不同:
    • layer不界说自己的外观
    • layer仅办理位图周围的状况信息。
    • 位图自身可所以视图制造自身的成果,也可所以指定的UIImage
    • 因而,在App中运用的首要layer被视为模型目标
      • 因为它们首要办理数据。因为它会影响动画的行为。

1.1 layer目标 作为 绘图动画的根底

1.1.1 依据layer的绘图模型

1.1.1 Core Animation制造内容进程:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

layer利用硬件加快烘托

  • layer目标捕获运用程序供给的内容并将其缓存在位图中。当咱们更改图层的特色时,更改的是与layer目标相关的状况信息。
  • 当更改触发动画时,Core Animation 会将layer的位图和状况信息传递给图形硬件,图形硬件将运用新信息进行烘托位图的作业
  • 操作的是静态位图

view在主线程上运用 CPU 烘托

  • 对view自身的更改一般会导致调用View的drawRect:办法以运用新参数从头制造内容。
  • 但这种办法的绘图成本很高,因为它是在主线程上运用 CPU 来完结
1.1.2 CALayer是显现的根底

咱们在探究 iOS图层烘托原理,关于这部分,现已做过翔实阐述,在这儿咱们直接概括定论:

CALayer 是显现的根底:存储 bitmap

  • CALayer 有一个特色 contents
  • contents 特色保存了由设备烘托流水线烘托好的位图 bitmap(一般也被称为 backing store
    • 而当设备屏幕进行改写时,会从 CALayer 中读取生成好的 bitmap,然后呈现到屏幕上
    • 在代码中对 CALayer 的 contents 特色进行了设置:
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • An object providing the contents of the layer, typically a CGImageRef.
    • contents 供给了 layer 的内容,是一个指针类型
      • 在 iOS 中的类型便是 CGImageRef(在 OS X 中还可所以 NSImage)。
      • Apple 对 CGImageRef 的界说是:A bitmap image or image mask.
  • 那么在运转时,操作体系会调用底层的接口,将 image 经过 CPU+GPU 的烘托流水线烘托得到对应的 bitmap,存储于 CALayer.contents 中,在设备屏幕进行改写的时分就会读取 bitmap 在屏幕上呈现。
  • 也正因为每次要被烘托的内容是被静态的存储起来的,所以每次烘托时,Core Animation 会触发调用 drawRect: 办法,运用存储好的 bitmap 进行新一轮的展现

1.1.2 依据layer的动画

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

  • layer目标的数据和状况信息(特色值)与该layer内容在屏幕上的视觉呈现别离。 咱们能够经过修正layer目标的特色值,来完结动画
  • 在动画进程中,Core Animation 会在硬件中完结一切逐帧制造。 咱们只需求指定动画参数,如:动画的起点和结尾、自界说计时信息和等
  • layer中的可动画特色:
    • anchorPoint
    • backgroundColor
    • backgroundFilters
    • borderColor
    • borderWidth
    • bounds
    • compositingFiltercontents
    • contentsRect
    • cornerRadius
    • doubleSided
    • filtersframehidden
    • mask
    • masksToBounds
    • opacity
    • position
    • shadowColor
    • shadowOffset
    • shadowOpacity
    • shadowPath
    • shadowRadius
    • sublayers
    • sublayerTransform
    • transform
    • zPosition
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

1.2 layer目标界说自己的几许形状

视觉几许包含有关该内容的borderboundspositiontransform(旋转、缩放或改换)、shadow

1.2.1 两种类型的坐标系

运用layer目标开发进程中,咱们会涉及到两套坐标系: 点坐标系, 单位坐标系。 其间点坐标系和咱们在用 UIKIt中的view开发时,相差无几

1. 点坐标系

  • 指定layer的巨细和方位,别离运用boundsposition特色
  • 界说bounds图层自身的坐标系并包含图层在屏幕上的巨细。该position特色界说图层相关于其父级坐标系的方位。
    @interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
    ...
    /** Geometry and layer hierarchy properties. **/
    /* The bounds of the layer. Defaults to CGRectZero. Animatable. */
    @property CGRect bounds;
    /* The position in the superlayer that the anchor point of the layer's
     * bounds rect is aligned to. Defaults to the zero point. Animatable. */
    @property CGPoint position;
    ...
    @end
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • 需求留意的一件事是该position特色坐落图层的中心。该特色是其界说依据图层anchorPoint特色中的值而改动的多个特色之一

2. 单位坐标系

中心动画运用单位坐标来表明特色,这些特色的值或许会随着图层巨细的改动而改动。

  • 锚点anchorPoint是咱们运用单位坐标系指定的多个特色之一。
  • 能够将单位坐标视为指定总或许值的百分比
  • 单位坐标空间中的每个坐标的规模为0.0到1.0。例如:
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
    • 沿 x 轴,左边际坐落坐标 处0.0,右边际坐落坐标 处1.0。
    • 沿y轴,单位坐标值的方向依据渠道的不同而改动

1.2.2 锚点影响几许操作

  • 运用图层的anchorPoint特色来访问该锚点
  • 图层的几许相关操作是相关于该图层的锚点进行
  • transform方位特色始终是相关于图层的锚点指定的,而且运用到图层的任何改换也相关于锚点发生

修正锚点值,影响旋转操作示例:

  • anchorPoint 从(0.5,0.5)改成(0.0,0.0)
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • 旋转作用改动:
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

1.3 三组layer树

运用 Core Animation 的运用程序具有三组图层目标。每组图层目标在使运用程序的内容显现在屏幕上方面都有不同的作用:

  • model layer 树 (“layer 树)
    • 运用程序交互最多的目标。
    • 此树中的目标是存储任何动画的目标值的模型目标。
    • 每当您更改图层的特色时,都会运用这些目标之一。
  • 演示树 presentation tree
    • 目标中包含任何正在运转的动画的运转中值
    • 图层树目标包含动画的目标值,而演示树中的目标反映屏幕上呈现的当时值
    • 内部特色可读不可写
    • 能够从这些值开端创立一个新动画
  • 烘托树render tree
    • 目标履行实践的动画,而且是中心动画私有的
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

1.4 UIView与CAlayer 的联系

UIView 作为最常用的视图控件,和 CALayer 也有着千丝万缕的联系
咱们在探究 iOS图层烘托原理,关于这部分,现已做过翔实阐述,在这儿咱们直接概括定论:

1.4.1 UIView的责任

  • Drawing and animation:制造与动画
  • Layout and subview management:布局与子 view 的办理
  • Event handling: 处理交互作业(如点击作业、旋转作业、press作业、加快作业、长途操控等)

1.4.2 CALayer的责任

  • CALayer 是 UIView 的特色之一,担任烘托和动画,供给可视内容的呈现
  • 咱们创立一个 UIView 的时分,UIView 会主动创立一个 CALayer,为自身供给存储 bitmap 的当地,并将自身固定设置为 CALayer 的署理

1.4.3 两个中心联系

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

  • CALayer 是 UIView 的特色之一,担任烘托动画,供给可视内容的呈现。
  • UIView 供给了对 CALayer 部分功用的封装,同时也别的担任了交互作业的处理
    • 为什么 UIKit 中的视图能够呈现可视化内容?便是因为 UIKit 中的每一个 UI 视图控件其实内部都有一个相关的 CALayer,即 backing layer
    • CALayer 事实上是用户所能在屏幕上看见的一切的根底

1.4.4 依据 两个中心联系 的拓宽

1.4.4.1 两者的异同

相同点:

  • 相同的层级结构:
    每个 UIView 都一一对应CALayer 担任页面的制造,所以视图层级具有 视图树 的树形结构,对应 CALayer 层级也具有 图层树 的树形结构
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
    • 其间,View的责任是 创立并办理 图层,以保证当子视图在层级联系中 增加或被移除 时,其相关的图层在图层树中也有相同的操作(即保证视图树和图层树在结构上的一致性)

不同点:

  • 部分作用的设置: 因为 UIView 只对 CALayer 的部分功用进行了封装,而另一部分如圆角暗影边框等特效都需求经过调用 layer 特色来设置。
  • 是否呼应点击作业: CALayer 不担任点击作业,所以不呼应点击作业,而 UIView 会呼应。
  • 不同承继联系:
    • CALayer 承继自 NSObject
    • UIView 因为要担任交互作业,所以承继自 UIResponder。
1.4.4.2 供给两个平行的层级联系的意义

为什么要将 CALayer 独立出来,直接运用 UIView 一致办理不行吗?为什么不必一个一致的目标来处理一切作业呢?

  • 这样规划的首要原因便是为了责任别离拆分功用便利代码的复用;
  • 经过 Core Animation 结构来担任可视内容的呈现,这样在 iOS 和 OS X 上都能够运用 Core Animation 进行烘托;
  • 与此同时,两个体系还能够依据交互规矩的不同来进一步封装一致的控件,比方 iOS 有 UIKit 和 UIView,OS X 则是AppKit 和 NSView。
  • 实践上,这儿并不是两个层级联系,而是四个。每一个layer都有三层树:layer树呈现树烘托树(除了 视图树图层树,还有 呈现树烘托树)

2. 设置layer目标

2.1 启用中心动画支撑

  • 链接到 QuartzCore 结构。(iOS 运用程序仅在显式运用 Core Animation 接口时才有必要链接到此结构。)
import UIKit
import QuartzCore
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 创立一个视图
        let redView = UIView(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
        redView.backgroundColor = UIColor.red
        self.view.addSubview(redView)
        // 创立基本动画
        let animation = CABasicAnimation(keyPath: "position")
        animation.fromValue = redView.layer.position
        animation.toValue = CGPoint(x: 200, y: 200)
        animation.duration = 1.0
        // 增加动画到视图的图层
        redView.layer.add(animation, forKey: "positionAnimation")
    }
}

在这个示例中,咱们运用了 CABasicAnimation 类来创立基本动画,而 redView 的图层是经过 redView.layer 特色来访问的,这就涉及到了 QuartzCore 结构。经过 Core Animation 的高级 API,咱们能够更加便利地创立和办理动画作用,而不必直接操作底层的 QuartzCore 结构

2.2 更改与View相关的layer目标

  • 图层支撑的视图会创立该类的实例CALayer
  • CoreAnimation供给了不同的layer类,每个layer类都供给了专门功用。
  • 挑选不同的layer类或许使咱们能够以简略的办法进步功能或支撑特定类型的内容。
  • 更改 UIView 运用的图层类,经过重写办法:
    + (Class)layerClass {
        return [CAMetalLayer class];
    }
    
  • 例如:运用 Metal 或 OpenGL ES 制造View的内容时,运用CAMetalLayer或CAEAGLLayer目标更适宜。
  • CALayer子类及其用处介绍
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

2.3 供给图层的内容|contents特色

2.3.1 设置图层内容

  • 运用 contents 特色设置图层的内容,可所以 CGImageUIImageUIColor 等类型
  • 这个特色的类型被界说为id,意味着它可所以任何类型的目标
    • 在这种情况下,能够给 contents 特色赋任何值,app 仍然能够编译经过
    • 可是,在实践中,假如给 contents 赋的不是CGImage, 那么得到的图层将是空白的
  • 事实上,实在要赋值的类型应该是CGImageRef,它是一个指向CGImage结构的指针。
  • UIImage有一个CGImage特色,它回来一个”CGImageRef”,假如想把这个值直接赋值给CALayer 的 contents ,那将会得到一个编译错误。
    • 因为CGImageRef并不是一个实在的 Cocoa目标,而是一个Core Foundation类型。
    • 能够经过__bridge关键字转化。假如要 给图层的寄宿图赋值,你能够依照以下这个办法:
    layer.contents = (__bridge id)image.CGImage;
    

代码如下:

class ViewController: UIViewController {
    lazy var v: UIView = {
        let v = UIView()
        v.backgroundColor = .white
        v.frame = CGRect.init(x: UIScreen.main.bounds.size.width / 2 - 100 , y: UIScreen.main.bounds.size.height / 2 - 100, width: 200, height: 200)
        return v
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(self.v)
        let image = UIImage.init(named: "hello")
        self.v.layer.contents = image?.cgImage
    }
}

运转成果如下:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

2.3.2 contentGravity特色

加载的图片并不 刚好是一个方的,为了习惯这个视图,它有一点点被拉伸了。在运用UIImageView 的时分遇到过相同的问题,解决办法便是把 contentMode 特色设置成更适宜的 值,像这样:

imageView.contentMode = .scaleAspectFill

CALayer与 contentMode 对应的特色叫做 contentsGravity

self.v.layer.contentsGravity = .resizeAspect

运转成果如下:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

将上面的contentGravity改一下:

self.v.layer.contentsGravity = .resizeAspectFill

运转作用如下:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

2.4 调整图层的视觉样式和外观

  • backgroundColor:设置图层的布景色彩。
    let layer = CALayer()
    layer.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
    layer.backgroundColor = UIColor.red.cgColor
    
  • borderColor 和 borderWidth:设置图层的边框色彩和宽度。
    layer.borderColor = UIColor.blue.cgColor
    layer.borderWidth = 2.0
    
  • cornerRadius:设置图层的圆角半径。
    layer.cornerRadius = 10.0
    
  • shadowColor、shadowOffset、shadowOpacity 和 shadowRadius:设置图层的暗影色彩、偏移、不通明度和半径。
    layer.shadowColor = UIColor.gray.cgColor
    layer.shadowOffset = CGSize(width: 0, height: 3)
    layer.shadowOpacity = 0.5
    layer.shadowRadius = 5.0
    
  • mask:设置图层的蒙版,用于裁剪图层内容。
    let maskLayer = CALayer()
    maskLayer.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
    maskLayer.backgroundColor = UIColor.black.cgColor
    layer.mask = maskLayer
    

3. Layer层级办理

3.1 修正层次结构的办法

与View中办理父子视图的API差不多:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

...
@property(nullable, readonly) CALayer *superlayer;
- (void)removeFromSuperlayer;
@property(nullable, copy) NSArray<__kindof CALayer *> *sublayers;
- (void)addSublayer:(CALayer *)layer;
- (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx;
- (void)insertSublayer:(CALayer *)layer below:(nullable CALayer *)sibling;
- (void)insertSublayer:(CALayer *)layer above:(nullable CALayer *)sibling;
- (void)replaceSublayer:(CALayer *)oldLayer with:(CALayer *)newLayer;

3.2 子层的定位和巨细调整

  • 设置子图层的巨细用bounds(等同于view中的bounds)
  • 运用该特色设置其在其superlayer中的方位position,(等同于view中的center)
myLayer.bounds = CGRectMake(0, 0, 100, 100);
myLayer.position = CGPointMake(200, 200);

3.3 子图层和剪辑

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

  • 启用剪切 layer.masksToBounds = YES
    • 图层剪切蒙版的形状包含图层的角半径(假如已指定)。图 4-3显现的图层演示了该masksToBounds特色怎么影响具有圆角的图层。
      当该特色设置为 时NO,子图层将完好显现,即便它们超出了父图层的鸿沟。更改特色会YES导致其内容被剪裁。

3.4 转化层之间的坐标值

- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)l;
- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r fromLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r toLayer:(nullable CALayer *)l;

4. transform改换

  • 运用CGAffineTransform 能够用来对图层旋转,摆放或许扭曲
  • 运用CATransform3D能够将扁平物体转化成三维空间目标 的

4.1 2D改换|CGAffineTransform

创立一个CGAffineTransform

Core Graphics供给了一系 列函数,对彻底没有数学根底的开发者也能够简略地做一些改换。如下几个函数都创立了一个 CGAffineTransform 实例:

// 1. 旋转改换
CGAffineTransformMakeRotation(CGFloat angle) 
// 2. 缩放改换
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) 
// 3. 平移改换
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
  • 旋转缩放改换都能够很好解释
    别离旋转或许缩放一个向量的值。
  • 平移改换是指每个点都移动了向量指定的x或许y值–所以假如向量代表了一个点,那它就平移了这个点的距离。

需求:将原始视图旋转45角度

  • UIView 能够经过设置 transform 特色做改换,但实践上它仅仅封装了内部图层 的改换。
  • CALayer 相同也有一个 transform 特色,但它的类型是 CATransform3D ,而不是 CGAffineTransform
  • CALayer 对应 于 UIView 的 transform 特色叫做 affineTransform
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_4);
self.layerView.layer.affineTransform = transform;
留意咱们运用的旋转常量是 M_PI_4 ,而不是你幻想的45,因为iOS的改换函数使 用弧度而不是角度作为单位。弧度用数学常量pi的倍数表明,一个pi代表180度,所 以四分之一的pi便是45度。
C的数学函数库(iOS会主动引进)供给了pi的一些简便的换算, M_PI_4 于是就 是pi的四分之一,假如对换算不太清楚的话,能够用如下的宏做换算:
#define RADIANS_TO_DEGREES(x) ((x)/M_PI*180.0)

4.2 混合改换

在一个改换的根底上做更深层次的改换

  • Core Graphics供给了一系列的函数能够在一个改换的根底上做更深层次的改换
  • 假如做一个既要缩放又要旋转的改换,这就会十分有用了。

下面函数:

CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy) 
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)

当操作一个改换的时分,初始生成一个什么都不做的改换很重要–也便是创立一 个 CGAffineTransform 类型的空值,矩阵论中称作单位矩阵,Core Graphics相同也供给了一个便利的常量:CGAffineTransformIdentity

终究,假如需求混合两个现已存在的改换矩阵,就能够运用如下办法,在两个改换的根底上创立一个新的改换:

CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);

用例1:运用若干办法创立一个复合改换

代码下:

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *v = [[UIView alloc]init];
    [self.view addSubview: v];
    v.backgroundColor = UIColor.redColor;
    v.frame = CGRectMake(150, 150, 100, 100);
    CGAffineTransform transform = CGAffineTransformIdentity;
    //scale by 50%
    transform = CGAffineTransformScale(transform, 0.5, 0.5);
    //rotate by 30 degrees
    transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0);
    //translate by 200 points
    transform = CGAffineTransformTranslate(transform, 200, 0);
    //apply transform to layer
    v.layer.affineTransform = transform;    
}

4.3 3D改换|CATransform3D

  • CGAffineTransform 类型归于Core Graphics结构
    • Core Graphics实践上是一个严格意义上的2D绘图API
    • 而且 CGAffineTransform 仅仅对2D改换有用
  • CGAffineTransformCATransform3D 的异同
    • CGAffineTransform 相似, CATransform3D 也是一个矩阵,可是和2×3的矩阵不同, CATransform3D 是一个能够在3维空间内做改换的4×4的矩阵。
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
    • CGAffineTransform 矩阵相似, Core Animation供给了一系列的办法用来创立和组合 CATransform3D 类型的矩阵,和Core Graphics的函数相似
    • 可是3D的平移和旋转多出了一个 z 参数,而且旋转函数除了 angle 之外多出 了 x , y , z 三个参数,别离决议了每个坐标轴方向上的旋转:
    CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
    CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) 
    CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
    

Z轴和这两个轴别离笔直,指向视角外为正方向。

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

用例1:对视图内的图层绕Y轴做45度角的旋转

CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
v.layer.transform = transform;

4.3.1 透视投影

  • CATransform3D 的透视作用经过一个矩阵中一个很简略的元从来操控:m34
  • m34用于按份额缩放XY的值来核算到底要离视角多远。
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • m34 的默许值是0,能够经过设置 m34 为-1.0 / d 来运用透视作用
  • d 代表了幻想中视角相机和屏幕之间的距离,以像素为单位,
    • 那应该怎么核算这个距离呢?
    • 实践上并不需求,大约预算一个就好了 【一般500-1000就现已很好了】

用例1:对图片做透视作用

override func viewDidLoad() {
    super.viewDidLoad()
    self.view.addSubview(imgView)
    //create a new transform
    var transform: CATransform3D = CATransform3DIdentity
    // 透视作用
    transform.m34 = -1.0 / 500
    //rotate by 45 degrees along the Y axis
    transform = CATransform3DRotate(transform, .pi / 4, 0, 1, 0)
    //apply to layer
    self.imgView.layer.transform = transform
}

5. CAlayer的常用特色

//宽度和高度
@property CGRect bounds;
//方位(默许指中点,具体由anchorPoint决议)
@property CGPoint position;
//锚点(x,y的规模都是0-1),决议了position的含义
@property CGPoint anchorPoint;
//布景色彩(CGColorRef类型)
@propertyCGColorRefbackgroundColor;
//形变特色
@property CATransform3D transform;
//边框色彩(CGColorRef类型)
@property  CGColorRef  borderColor;
//边框宽度
@property CGFloat borderWidth;
//圆角半径
@property CGFloat cornerRadius;
//内容(比方设置为图片CGImageRef)
@property(retain) id contents;

6. Hit Testing

CALayer 并不关心任何呼应链作业,所以不能直接处理接触作业或许手势。可是它有两个API处理作业: -containsPoint: 和 -hitTest:

  • -containsPoint: 接受一个在本图层坐标系下的 CGPoint
    • 假如这个点在图层 frame 规模内就回来 YES 。
    • 也便是运用-containsPoint: 办法来判断到底是赤色仍是蓝色的图层被接触了
  • -hitTest:办法相同接受一个 CGPoint 类型参数,
    • 它回来图层自身,或许包含这个坐标点的sublayer。
    • 这意味着不再需求像运用 - containsPoint:
    • 那样,人工地在每个子图层改换或许测验点击的坐标。假如这个点在最外面图层的规模之外,则回来nil。

事例:

class ViewController: UIViewController {
    lazy var blueLayer = CALayer()
    lazy var v: UIView = {
        let v = UIView()
        v.backgroundColor = .red
        v.frame = CGRect.init(x: UIScreen.main.bounds.size.width / 2 - 100 , y: UIScreen.main.bounds.size.height / 2 - 100, width: 200, height: 200)
        return v
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(self.v)
        let blueLayer = CALayer()
        blueLayer.frame = CGRect.init(x: 50, y: 50, width: 100, height: 100)
        blueLayer.backgroundColor = UIColor.blue.cgColor
        blueLayer.delegate = self
        self.blueLayer = blueLayer
        self.v.layer.addSublayer(blueLayer)
    }
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        //得到在主view中的position
        guard var point = touches.first?.location(in: self.view) else { return }
        //转化到v.layer的方位
        point = self.v.layer.convert(point, from: self.view.layer)
        if self.v.layer.contains(point) {
            point = self.blueLayer.convert(point, from: self.v.layer)
            if self.blueLayer.contains(point) {
                print("点击了蓝色")
            } else {
                print("点击了赤色")
            }
        }
    }
}

事例2:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

7. 其它layer目标介绍

Class Usage
CAEmitterLayer 用于完结依据 Core Animation 的粒子发射器体系。发射器层目标操控粒子的生成及其来历。
CAGradientLayer 用于制造填充图层形状的色彩突变(在任何圆角的规模内)。
CAMetalLayer 用于设置和 vend 可制造纹路,以运用 Metal 烘托图层内容。
CAEAGLLayer/CAOpenGLLayer 用于设置备份存储和上下文,以运用 OpenGL ES(iOS)或 OpenGL(OS X)烘托图层内容。
CAReplicatorLayer 当你要主动制造一个或多个子层的副本时运用。仿制器为你制造副本,并运用你指定的特色来更改副本的 appearance 或 attributes。
CAScrollLayer 用于办理由多个子层组成的较大的可翻滚区域。
CAShapeLayer 用于制造三次贝塞尔曲线样条曲线。Shape 图层关于制造依据途径的形状十分有利,因为它们始终会发生明晰的途径,而与你制造到图层的备份存储中的途径相反,后者在缩放时看起来并不好。可是,明晰的成果确实涉及在主线程上烘托 Shape 并缓存成果。
CATextLayer 用于呈现纯文本字符串或特色字符串。
CATiledLayer 用于办理可分为较小图块的大图画,并支撑扩大和缩小内容,别离烘托。
CATransformLayer 用于烘托实在的 3D 图层层次结构,而不是由其他图层类完结的平坦的图层层次结构。
QCCompositionLayer 用于烘托 Quartz Composer 组成。(仅支撑 OS X)

7.1 CAShapeLayer|CALayer

  • CAShapeLayer 是一个经过矢量图形而不是bitmap来制造的图层子类。
  • 指定比方色彩colorlinewidth线宽等特色,用 CGPath 来界说想要制造的图形,终究CAShapeLayer 就主动烘托出来了
    • CGPath 能表明的形状,CAShapeLayer 都能够制造出来。
    • 换句话说CGPath能够约束CAShapeLayer的形状。
    • CAShapeLayer有一个特色Path,将途径赋值给这个特色即可。
  • 也能够用Core Graphics直接向原始的 CALyer 的内容中制造一个途径,相比之下,运用 CAShapeLayer 有以下一些优点:
    • 烘托快速:
      • CAShapeLayer 运用了硬件加快,制造同一图形会比用Core Graphics快许多
    • 高效运用内存:
      • 一个 CAShapeLayer 不需求像一般 CALayer 相同创立一个寄宿图形,所以无论有多大,都不会占用太多的内存
    • 不会被图层鸿沟剪裁掉:
      • 一个 CAShapeLayer 能够在鸿沟之外制造。
      • 你的图层途径不会像在运用Core Graphics的一般 CALayer 相同被剪裁掉。
    • 不会呈现像素化:
      • 当你给 CAShapeLayer 做3D改换时,它不像一个有寄宿图 的一般图层相同变得像素化。

一些运用场景:

    1. 作为遮罩:
    • CAShapeLayer能够作为其他图层的遮罩运用,用于约束其他图层的形状
    • 经过图层的mask特色赋值。
      /*
      下面是制造一个圆形的图片
      一般咱们经过设置imageView.layer的圆角半径来让imageView变成圆形
      现在能够直接运用CAShapeLayer生成一个圆形遮罩覆盖在imageView上
      */
      //创立imageView
      UIImageView *imagev = [[UIImageView alloc] init];
      imagev.frame = CGRectMake(100, 100, 100, 100);   //边长100的正方形
      [self.view addSubview:imagev];
      CAShapeLayer *shaplayer = [CAShapeLayer layer];
      UIBezierPath *path = [UIBezierPath bezierPath];     //创立途径
      [path addArcWithCenter:CGPointMake(50, 50) radius:50 startAngle:0 endAngle:M_PI*2 clockwise:YES];       //圆形途径 留意这儿的center是以imageView为坐标系的
      shaplayer.path = path.CGPath;    //要转成CGPath
      imagev.layer.mask = shaplayer;   //约束imageView的外形
      
    • 留意:
      • 作为遮罩时不必设置色彩特色,只需设置path特色。
      • 作为遮罩时才会约束父layer的形状
      • 作为子layer时不会约束父layer的形状
    1. 动画作用:
    • 经过不断的改动CAShapeLayer的path然后达到动画的作用
    • 能够做出中心动画难以完结的作用,比方
      • 粘性动画单边的弹性下拉作用qq的粘性按钮作用正弦波涛线等等,相当丰厚,我这儿供给几个链接
        粘性动画
    1. 两个特色strokeStart和strokeEnd:
    • 这两个特色用于对制造的Path进行区域约束,值为0-1之间,而且这两个特色可做动画,比方如下。
    - (void)viewDidLoad {
        [super viewDidLoad];
        CAShapeLayer *layer = [CAShapeLayer layer];
        layer.strokeColor = kRedColor.CGColor;
        layer.fillColor = kClearColor.CGColor;
        layer.lineWidth = 2;   //经过调整线宽能够做成饼状图
        UIBezierPath *path = [UIBezierPath bezierPath];
        //值得一提的是这儿圆的起点是-
        [path addArcWithCenter:CGPointMake(150, 150) radius:100 startAngle:-M_PI endAngle:M_PI clockwise:YES];
        layer.path = path.CGPath;
        layer.strokeEnd = 0.0;
        self.layer = layer;
        [self.view.layer addSublayer:layer];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        CABasicAnimation *anim = [CABasicAnimation animation];
        anim.keyPath = @"strokeEnd";//KVC
        anim.fromValue = @0.0;
        anim.toValue = @1.0;
        anim.duration = 2;
        anim.repeatCount = 100;
        [self.layer addAnimation:anim forKey:nil];   
    }
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
    1. 虚线作用:
    • 虚线作用只需设置lineDashPattern 特色:
      • self.shapLayer.lineDashPattern = @[@(4),@(4)];
    • 数组中第一个4表明先画4个点的实线,第二4表明接着距离4个点不画线
    1. 二维码扫描框: 二维码的扫描框一般是中心矩形为通明,其他边框为带通明度的黑色
    //包裹self.view的途径
    UIBezierPath *overlayPath = [UIBezierPath bezierPathWithRect:self.view.bounds];
    //中心通明边框的途径
    UIBezierPath *transparentPath = [UIBezierPath bezierPathWithRect:CGRectMake(100, 150, 200, 200)];
    //合并为一个途径
    [overlayPath appendPath:transparentPath];
    CAShapeLayer *layer = [CAShapeLayer layer];
    layer.path = overlayPath.CGPath;
    layer.fillRule = kCAFillRuleEvenOdd;  //奇偶填充规矩
    layer.fillColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.3].CGColor;
    [self.view.layer addSublayer:layer];
    

7.2 CATextLayer|CALayer

  • CALayer 的子类 CATextLayer以图层的形式包含了 UILabel 几乎一切的制造特性,而且额定供给了一些新的特性。
  • CATextLayer也要比 UILabel 烘托得快得多。
    • 很少有人知道在iOS 6及之前的版本, UILabel 其实是经过WebKit来完结制造的,这样就形成了当有许多文字的时分就会有极大的功能压力。
    • 而 CATextLayer 运用了Core text,而且烘托得 十分快。
  • CATextLayer 显现文字示例:
    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.string = @"123abcABC123abcABC123abcABC123abcABC123abcABC123abcABC123a3abcABC123abcABC123abcABC123a3abcABC123abcABC123abcABC123abcABC123abcABC呵呵呵";
    textLayer.font = CGFontCreateWithFontName((__bridge CFStringRef)(@"Georgia"));
    textLayer.fontSize = 12;
    textLayer.backgroundColor = kYellowColor.CGColor;
    textLayer.foregroundColor = kRedColor.CGColor;         //文字色彩,一般字符串时能够运用该特色
    textLayer.wrapped = YES;                               //为yes时主动换行
    textLayer.truncationMode = @"start";                   //字符串过长时的省掉方位,留意是终究一行的哪个方位
    //    textLayer.alignmentMode = kCAAlignmentCenter;        //对齐办法
    //    textLayer.allowsFontSubpixelQuantization = NO;
    textLayer.frame = CGRectMake(0, 0, 100, 100);
    //    textLayer.position = self.view.center;                //图层的中心点坐落父层的方位
    textLayer.contentsScale = [UIScreen mainScreen].scale;  //按当时的屏幕分辨率显现   不然字领会含糊
    [self.view.layer addSublayer:textLayer];
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

7.3 CATransformLayer|CALayer

  • CATransformLayer是一个容器layer
    • backgroundColor等外观显现特色对他是无效的,
  • CATransformLayer 仅仅一个容器,
    • 只担任容纳其他layer并显现其他layer
    • CATransformLayer一般用于结构复杂的3D事物,他不是一个平面化的图层,能够结构多层次的3D结构。
    //这儿就创立一个简略的立方体,代码如下
    @interface ViewController ()
    @property (nonatomic,strong) NSMutableArray<CALayer *> *layerArray;
    @property (nonatomic,strong) NSMutableArray *transArray;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        //创立图层
        CATransformLayer *cublayer = [CATransformLayer layer];
    //    layer.borderColor = kBlackColor.CGColor;      //这些特色设置都是无效的
    //    layer.borderWidth = 1;
    //    layer. backgroundColor = [UIColor redColor].CGColor;
        cublayer.bounds = CGRectMake(0, 0, 200, 200);
        cublayer.position = self.view.center;
        [self.view.layer addSublayer:cublayer];
        //对容器图层做动画
        CATransform3D transA = CATransform3DMakeRotation(M_PI, 1, 1, 0);
        CATransform3D transB = CATransform3DMakeRotation(M_PI*2, 1, 1, 0);
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
        animation.duration  = 4;
        animation.autoreverses     = YES;
        animation.repeatCount     = 100;
        animation.fromValue     = [NSValue valueWithCATransform3D:transA];
        animation.toValue      = [NSValue valueWithCATransform3D:transB];
        [cublayer addAnimation:animation forKey:nil];
        //创立立方体的6个面
        self.layerArray = [NSMutableArray array];
        for (NSInteger i = 0; i<6; i++) {
            CALayer *sublayer = [CALayer layer];
            sublayer.bounds = CGRectMake(0, 0, 100, 100);
            sublayer.position = CGPointMake(100, 100);
            sublayer.backgroundColor = kRandomColorAndAlpha(0.3).CGColor;
            sublayer.speed = 0.1;
            [self.layerArray addObject:sublayer];
            [cublayer addSublayer:sublayer];
        }
        //为六个面的图层创立3D改换,使之组成立方体
        CATransform3D ct1 = CATransform3DMakeTranslation(0, 0, 50);
        CATransform3D ct2 = CATransform3DMakeTranslation(0, 0, -50);
        CATransform3D ct3 = CATransform3DMakeTranslation(-50, 0, 0);
        ct3 = CATransform3DRotate(ct3, M_PI_2, 0, 1, 0);
        CATransform3D ct4 = CATransform3DMakeTranslation(50, 0, 0);
        ct4 = CATransform3DRotate(ct4, M_PI_2, 0, 1, 0);
        CATransform3D ct5 = CATransform3DMakeTranslation(0, -50, 0);
        ct5 = CATransform3DRotate(ct5, M_PI_2, 1, 0, 0);
        CATransform3D ct6 = CATransform3DMakeTranslation(0, 50, 0);
        ct6 = CATransform3DRotate(ct6, M_PI_2, 1, 0, 0);
        //存入数组待用
        self.transArray = [NSMutableArray arrayWithArray:@[[NSValue valueWithCATransform3D:ct1],
                                                        [NSValue valueWithCATransform3D:ct2],
                                                        [NSValue valueWithCATransform3D:ct3],
                                                        [NSValue valueWithCATransform3D:ct4],
                                                        [NSValue valueWithCATransform3D:ct5],
                                                        [NSValue valueWithCATransform3D:ct6]]];
    }
    //一开端六个面叠在一同,点击屏幕后,立方体的六个面渐渐归位
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        for (NSInteger i = 0; i<6; i++) {
            NSValue *value = self.transArray[I];
            CATransform3D ct = [value CATransform3DValue];
            CALayer *layer = self.layerArray[I];
            [UIView animateWithDuration:1.0 animations:^{
                layer.transform = ct;
            }];
        } 
    }
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

7.4 CAGradientLayer|CALayer

CAGradientLayer是用来生成突变图层。两种更多色彩滑润突变的图层

  • 特色locations
    • locations 表明的是突变区间,数组中的数字有必要是递加的。比方
      • 下面这个比方layer.locations = @[@0.5,@0.8];
      • 突变区间是0.5-0.8,也便是说0.0-0.5是纯赤色,0.5-0.8是赤色突变到绿色,0.8-1.0是纯绿色。
      • 不设置这个特色便是整个区间0.0-1.0均匀突变。
    • locations 数组并不是强制要求的,可是假如你给它赋值了就一定要
      • 保证 locations 的数组巨细和 colors 数组巨细一定要相同
      • 不然你将会得到一个空白的突变。
    • locations 特色是一个浮点数值的数组 (以 NSNumber 包装), 0.0代表着突变的开端,1.0代表着完毕
  • startPointendPoint 特色
    • 他们决议了突变的方向。
    • 这两个参数是以单位坐标系进行的界说,所以左上角坐标是{0, 0},右下角坐标 是{1, 1}

事例1:红黄绿色彩突变:

  • 从红到黄终究到绿色的突变。
  • locations 数组指定了0.0,0.25和0.5三个数值,这样这三个突变就有点像挤在了左上角
    class ViewController: UIViewController {
        lazy var containV: UIView = {
            let v = UIView()
            v.frame = CGRect(x: 80, y: 150, width: 200, height: 200)
            return v
        }()
        override func viewDidLoad() {
            super.viewDidLoad()
            self.view.addSubview(containV)
            let gradientLayer: CAGradientLayer = CAGradientLayer()
            gradientLayer.frame = self.self.containV.bounds
            self.containV.layer.addSublayer(gradientLayer)
            let startColor = UIColor.red.cgColor
            let minddleColor = UIColor.yellow.cgColor
            let endColor = UIColor.green.cgColor
            gradientLayer.colors = [startColor, minddleColor, endColor]
            gradientLayer.locations = [0.0, 0.25, 0.5]
            gradientLayer.startPoint = CGPoint(x: 0, y: 0)
            gradientLayer.endPoint = CGPoint(x: 1, y: 1)
        }
    }
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

事例2:两种色彩的对角线突变:

class ViewController: UIViewController {
    lazy var containV: UIView = {
        let v = UIView()
        v.frame = CGRect(x: 80, y: 150, width: 200, height: 200)
        return v
    }()
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.addSubview(containV)
        let gradientLayer: CAGradientLayer = CAGradientLayer()
        gradientLayer.frame = self.self.containV.bounds
        self.containV.layer.addSublayer(gradientLayer)
        let startColor = UIColor.red.cgColor
        let endColor = UIColor.blue.cgColor
        gradientLayer.colors = [startColor,endColor]
        gradientLayer.startPoint = CGPoint(x: 0, y: 0)
        gradientLayer.endPoint = CGPoint(x: 1, y: 1)
    }
}

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

运用Core Graphics相关办法完结突变

  • iOS Core Graphics中有两个办法用于制造突变色彩:
    • CGContextDrawLinearGradient 能够用于生成线性突变
    • CGContextDrawRadialGradient 用于生成圆半径方向色彩突变
  • 函数能够自界说path,无论是什么形状都能够,原理都是用来做Clip,所以需求在CGContextClip函数前调用CGContextAddPath函数把CGPathRef加入到Context中。
  • 别的一个需求留意的当地是突变的方向,方向是由两个点操控的,点的单位便是坐标。
    • 因而需求正确从CGPathRef中找到正确的点,办法当然有许多种看具体完结,本例中,我便是简略得经过调用CGPathGetBoundingBox函数,回来CGPathRef的矩形区域,然后依据这个矩形取两个点
// 线性突变
- (void)drawLinearGradient:(CGContextRef)context
                      path:(CGPathRef)path
                startColor:(CGColorRef)startColor
                  endColor:(CGColorRef)endColor
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };
    NSArray *colors = @[(__bridge id) startColor, (__bridge id) endColor];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
    CGRect pathRect = CGPathGetBoundingBox(path);
    //具体方向可依据需求修正
    CGPoint startPoint = CGPointMake(CGRectGetMinX(pathRect), CGRectGetMidY(pathRect));
    CGPoint endPoint = CGPointMake(CGRectGetMaxX(pathRect), CGRectGetMidY(pathRect));
    CGContextSaveGState(context);
    CGContextAddPath(context, path);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
    CGContextRestoreGState(context);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}
- (void)viewDidLoad 
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //创立CGContextRef
    UIGraphicsBeginImageContext(self.view.bounds.size);
    CGContextRef gc = UIGraphicsGetCurrentContext();
    //创立CGMutablePathRef
    CGMutablePathRef path = CGPathCreateMutable();
    //制造Path
    CGRect rect = CGRectMake(0, 100, 300, 200);
    CGPathMoveToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPathAddLineToPoint(path, NULL, CGRectGetMidX(rect), CGRectGetMaxY(rect));
    CGPathAddLineToPoint(path, NULL, CGRectGetWidth(rect), CGRectGetMaxY(rect));
    CGPathCloseSubpath(path);
    //制造突变
    [self drawLinearGradient:gc path:path startColor:[UIColor greenColor].CGColor endColor:[UIColor redColor].CGColor];
    //留意开释CGMutablePathRef
    CGPathRelease(path);
    //从Context中获取图画,并显现在界面上
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageView *imgView = [[UIImageView alloc] initWithImage:img];
    [self.view addSubview:imgView];
}
圆半径方向突变
- (void)drawRadialGradient:(CGContextRef)context
                      path:(CGPathRef)path
                startColor:(CGColorRef)startColor
                  endColor:(CGColorRef)endColor
{
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat locations[] = { 0.0, 1.0 };
    NSArray *colors = @[(__bridge id) startColor, (__bridge id) endColor];
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
    CGRect pathRect = CGPathGetBoundingBox(path);
    CGPoint center = CGPointMake(CGRectGetMidX(pathRect), CGRectGetMidY(pathRect));
    CGFloat radius = MAX(pathRect.size.width / 2.0, pathRect.size.height / 2.0) * sqrt(2);
    CGContextSaveGState(context);
    CGContextAddPath(context, path);
    CGContextEOClip(context);
    CGContextDrawRadialGradient(context, gradient, center, 0, center, radius, 0);
    CGContextRestoreGState(context);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorSpace);
}
- (void)viewDidLoad 
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    //创立CGContextRef
    UIGraphicsBeginImageContext(self.view.bounds.size);
    CGContextRef gc = UIGraphicsGetCurrentContext();
    //创立CGMutablePathRef
    CGMutablePathRef path = CGPathCreateMutable();
    //制造Path
    CGRect rect = CGRectMake(0, 100, 300, 200);
    CGPathMoveToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
    CGPathAddLineToPoint(path, NULL, CGRectGetMidX(rect), CGRectGetMaxY(rect));
    CGPathAddLineToPoint(path, NULL, CGRectGetWidth(rect), CGRectGetMaxY(rect));
    CGPathAddLineToPoint(path, NULL, CGRectGetWidth(rect), CGRectGetMinY(rect));
    CGPathCloseSubpath(path);
    //制造突变
    [self drawRadialGradient:gc path:path startColor:[UIColor greenColor].CGColor endColor:[UIColor redColor].CGColor];
    //留意开释CGMutablePathRef
    CGPathRelease(path);
    //从Context中获取图画,并显现在界面上
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    UIImageView *imgView = [[UIImageView alloc] initWithImage:img];
    [self.view addSubview:imgView];
}

7.5 CAReplicatorLayer|CALayer

CAReplicatorLayer又是一个容器图层(仿制图层),他能够将他的子图层仿制指定的次数,仿制出来的这些图层都具有相同的图层特色和动画特色,经过下面的比方来介绍一些重要的特色

1. 辐射动画:

  • 特色instanceCount表明拷贝图层的次数,默许为1 。 举个比方instanceCount = 6 表明总共有6个子图层,其间5个是拷贝出来的。
  • 特色 instanceDelay 表明拷贝延时,拷贝一个图层后延时多少秒拷贝下一个图层 这儿为了使动画接连,我让动画的duration = 0.6 * 6 = 3.6
    //创立仿制图层容器
    CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
    replicator.frame = self.view.bounds;
    [self.view.layer addSublayer:replicator];
    replicator.instanceCount = 6;
    replicator.instanceDelay = 0.6;
    //扩大动画
    CABasicAnimation *anim = [CABasicAnimation animation];
    anim.keyPath = @"transform.scale";
    anim.fromValue = @1;
    anim.toValue = @20;
    anim.duration = 3.6;
    //通明度动画
    CABasicAnimation *anim2 = [CABasicAnimation animation];
    anim2.keyPath = @"opacity";
    anim2.toValue = @0.0;
    anim2.fromValue = @1.0;
    anim2.duration = 3.6;
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.animations = @[anim,anim2];
    group.duration = 3.6;
    group.repeatCount = 100;
    //创立子图层
    CALayer *layer = [CALayer layer];
    [layer addAnimation:group forKey:nil];
    layer.bounds = CGRectMake(0, 0, 10, 10);
    layer.position = self.view.center;
    layer.cornerRadius = 5;
    layer.backgroundColor = kRedColor.CGColor;
    [replicator addSublayer:layer];
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
    • 更改一下特色replicator.instanceCount = 3; 动画就不接连了
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

2. 加载动画:

  • 特色instanceTransform 表明仿制图层在被创立时发生的和上一个仿制图层的位移
    CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
    replicator.frame = self.view.bounds;
    [self.view.layer addSublayer:replicator];
    replicator.instanceCount = 6;
    replicator.instanceDelay = 0.2;
    //位移特色
    CATransform3D trans = CATransform3DMakeTranslation(25, 0, 0); //圆点顺次向右移动25
    replicator.instanceTransform = trans;
    //通明度动画
    CAKeyframeAnimation *anim = [CAKeyframeAnimation animation];
    anim.keyPath = @"opacity";
    anim.values = @[@1.0,@0.0,@1.0];
    anim.duration = 1.2;
    anim.repeatCount = 100;
    CALayer *layer = [CALayer layer];
    [layer addAnimation:anim forKey:nil];
    layer.bounds = CGRectMake(0, 0, 20, 20);
    layer.position = self.view.center;
    layer.cornerRadius = 10;
    layer.backgroundColor = kRedColor.CGColor;
    [replicator addSublayer:layer];
    

3. 其他的一些特色:

  • instanceColor : 设置多个仿制图层的色彩,默许位白色
  • //RGB偏移量
    • instanceRedOffset: 设置每个仿制图层相对上一个仿制图层的赤色偏移量
    • instanceGreenOffset: 设置每个仿制图层相对上一个仿制图层的绿色偏移量
    • instanceBlueOffset: 设置每个仿制图层相对上一个仿制图层的蓝色偏移量
    • instanceAlphaOffset: 设置每个仿制图层相对上一个仿制图层的通明度偏移量 以下便是设置instanceAlphaOffset = -0.1的作用,其他几个特色用法相似
      CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
          replicator.frame = self.view.bounds;
          [self.view.layer addSublayer:replicator];
          replicator.instanceCount = 6;
          replicator.instanceAlphaOffset = -0.1; // 通明度递减,每个图层都比上一个仿制图层的通明度小0.1
          CATransform3D trans = CATransform3DMakeTranslation(25, 0, 0);
          replicator.instanceTransform = trans;
          CALayer *layer = [CALayer layer];
          layer.bounds = CGRectMake(0, 0, 20, 20);
          layer.position = self.view.center;
          layer.cornerRadius = 10;
          layer.backgroundColor = kRedColor.CGColor;
          [replicator addSublayer:layer];
      
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

4. 侧重介绍一下instanceTransform特色:

CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
replicator.frame = self.view.bounds;
[self.view.layer addSublayer:replicator];
replicator.instanceCount = 2;
CATransform3D trans = CATransform3DMakeTranslation(0, -50, 0);  //y的负方向平移50  也便是方块的上方
trans = CATransform3DRotate(trans, M_PI_4, 0, 0, 1);    //然后旋转45度
replicator.instanceTransform = trans;
CALayer *layer = [CALayer layer];
layer.bounds = CGRectMake(0, 0, 30, 30);
layer.position = self.view.center;
layer.backgroundColor = kRedColor.CGColor;
[replicator addSublayer:layer];

咱们来看看instanceTransform是怎样运作的:

  • 先设置 replicator.instanceCount = 2;
  • 作用如下,很明显上面那个小方块向上平移了50个点然后旋转了45度。
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • 设置 replicator.instanceCount = 3;
    • 作用如下,因为方块2旋转了45度,所以方块2的上方(黑色边表明上方)也是旋转之后的上方,方块3便是沿着方块2的上方平移50点然后再旋转45度。
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • 设置replicator.instanceCount = 4; 经过上面的揣度,下面的作用应该能自己想出来了。
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • 这儿要特别留意CATransform3DRotate旋转改换,该方块旋转之后自己的坐标系也发生了相同角度的旋转(感觉是每个方块都有自己的坐标系),在旋转之后再要进行平移操作,那也是依照旋转之后的坐标系进行平移。 (上面的揣度纯属个人判断,如有错误还望指正!)
    CATransform3D trans = CATransform3DMakeTranslation(0, -50, 0);
    trans = CATransform3DRotate(trans, M_PI_4, 0, 0, 1);
    trans = CATransform3DTranslate(trans, 21, 0, 0);  //依照上面旋转之后的坐标系进行平移(当时坐标系的右方向平移21)
    
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

7.6 CAScrollLayer|CALayer

- (void)viewDidLoad {
    [super viewDidLoad];
    CALayer *layer = [CALayer layer];
    layer.contents = (id)kImage(@"111").CGImage;
    layer.frame = CGRectMake(0, 0, 375, 667);
    self.scrollLayer = [CAScrollLayer layer];
    self.scrollLayer.frame = CGRectMake(60, 60, 200, 200);
    [self.scrollLayer addSublayer:layer];
    self.scrollLayer.scrollMode = kCAScrollBoth;
    [self.view.layer addSublayer:self.scrollLayer];
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(gestureChanged:)];
    [self.view addGestureRecognizer:pan];
}
-(void)gestureChanged:(UIPanGestureRecognizer *)gesture
{
    CGPoint translation = [gesture translationInView:self.view];
    CGPoint origin = self.scrollLayer.bounds.origin;
    origin = CGPointMake(origin.x-translation.x, origin.y-translation.y);
    [self.scrollLayer scrollToPoint:origin];
    [gesture setTranslation:CGPointZero inView:self.view];
}

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

7.7 CAEmitterLayer|CALayer

  • CAEmitterLayer 是一个高功能的粒子引擎,被用来创立 实时粒子动画如: 烟雾火花等等这些作用
  • CAEmitterLayer 常与 CAEmitterCell 结合运用
    • 你将会为不同的比方作用界说一个或多个CAEmitterCell作为模版,
    • 同时CAEmitterLayer担任依据这些模版实例化一个粒子流。
    • 一个CAEmitterCell相似于一个CALayer:
      它有一个contents特色能够界说为一个CGImage,别的还有一些可设置特色操控着体现和行为。

7.7.1 CAEmitterLayer

  • renderMode:烘托形式,操控着在视觉上粒子图片是怎么混合的。
    NSString * const kCAEmitterLayerUnordered;
    NSString * const kCAEmitterLayerOldestFirst;
    NSString * const kCAEmitterLayerOldestLast;
    NSString * const kCAEmitterLayerBackToFront;
    NSString * const kCAEmitterLayerAdditive;
    
  • emitterMode: 发射形式,这个字段规定了在特定形状上发射的具体形式是什么
    kCAEmitterLayerPoints: 点形式,发射器是以点的局势发射粒子。
    kCAEmitterLayerOutline:这个形式下整个边框都是发射点,即边框进行发射
    kCAEmitterLayerSurface:这个形式下是咱们边框包含下的区域进行抛洒
    kCAEmitterLayerVolume: 同上
    
  • emitterShape:规定了发射源的形状。
    kCAEmitterLayerPoint:点形状,发射源的形状便是一个点,方位在上面position设置的方位
    kCAEmitterLayerLine:线形状,发射源的形状是一条线,方位在rect的横向的坐落笔直方向中心那条
    kCAEmitterLayerRectangle:矩形状,发射源是一个矩形,便是上面生成的那个矩形rect
    kCAEmitterLayerCuboid:立体矩形形状,发射源是一个立体矩形,这儿要收效的话需求设置z方向的数据,假如不设置就同矩形状
    kCAEmitterLayerCircle:圆形形状,发射源是一个圆形,形状为矩形包裹的那个圆,二维的
    kCAEmitterLayerSphere:立体圆形,三维的圆形,相同需求设置z方向数据,不设置则通二维相同
    
  • emitterSize:发射源的巨细,这个emitterSize结合position构建了发射源的方位及巨细的矩形区域rect
  • emitterPosition:发射点的方位。
  • lifetime:粒子的生命周期。
  • velocity:粒子速度。
  • scale:粒子缩放份额。
  • spin:自旋转速度。
  • seed:用于初始化发生的随机数发生的种子。
  • emitterCells:CAEmitterCell目标的数组,被用于把粒子投放到layer上

7.7.2. CAEmitterCell

  • 粒子在X.Y.Z三个方向上的加快度。
    @property CGFloat xAcceleration;
    @property CGFloat yAcceleration;
    @property CGFloat zAcceleration;
    
  • 粒子缩放份额、缩放规模及缩放速度。(0.0`1.0)
    @property CGFloat scale;
    @property CGFloat scaleRange;
    @property CGFloat scaleSpeed;
    
  • 粒子自旋转速度及规模:
    @property CGFloat spin;
    @property CGFloat spinRange;
    
  • 粒子RGB及alpha改动规模、速度。
    //规模:
    @property float redRange;
    @property float greenRange;
    @property float blueRange;
    @property float alphaRange;
    //速度:
    @property float redSpeed;
    @property float greenSpeed;
    @property float blueSpeed;
    @property float alphaSpeed;
    
  • emitterCells:子粒子。
  • color:指定了一个能够混合图片内容色彩的混合色。
  • birthRate:粒子发生系数,默许1.0.
  • contents:是个CGImageRef的目标,即粒子要展现的图片;
  • emissionRange:值是2(代码写成M_PI * 2.0f),这意味着粒子能够从360度恣意方位反射出来。假如指定一个小一些的值,就能够创造出一个圆锥形。
  • 指定值在时刻线上的改动,例如:alphaSpeed = 0.4,阐明粒子每过一秒减小0.4。

7.7.3 留意

  • CAEmitterLayerCAEmitterCell 中 有相同的特色,他们操控相同的特性
  • 若是相同特色都各自设置了值,粒子发射引擎在作业的时分,会把两个值相乘。作为这个特色的终究值来操控显现作用
  • 相同特色如下:
    @property float birthRate; // 每秒发生的粒子数量
    @property float lifetime; // 粒子的生命周期.单位是秒
    @property CGFloat scale; // 粒子的缩放份额
    

代码示例:

    UIView * containView = [[UIView alloc]initWithFrame:self.view.bounds];
    containView.center = self.view.center;
    containView.backgroundColor = self.view.backgroundColor;
    self.containView = containView;
    [self.view addSubview:self.containView];
    CAEmitterLayer *emitter = [CAEmitterLayer layer];
    emitter.frame = self.containView.bounds;
    [self.containView.layer addSublayer:emitter];
    emitter.renderMode = kCAEmitterLayerAdditive;//这会让堆叠的当地变得更亮一些。
    emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height / 2.0);
    CAEmitterCell *cell = [[CAEmitterCell alloc] init];
    cell.contents = (__bridge id)[UIImage imageNamed:@"star_yellow"].CGImage;
    cell.birthRate = 150;
    cell.lifetime = 5.0;
    cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor;
    cell.alphaSpeed = -0.4;
    cell.velocity = 50;
    cell.velocityRange = 50;
    cell.emissionRange = M_PI * 2.0;
    emitter.emitterCells = @[cell];

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

事例2:瀑布飘洒作用

- (void)setupSubviews {
    self.layer.backgroundColor = [UIColor blackColor].CGColor;
    // 装备emitter
    [self emiterLayer].renderMode = kCAEmitterLayerAdditive; // 粒子怎么混合, 这儿是直接堆叠
    [self emiterLayer].emitterPosition = CGPointMake(self.frame.size.width, 0); // 发射点的方位
    [self emiterLayer].emitterShape = kCAEmitterLayerPoint;
    NSMutableArray * mArr = @[].mutableCopy;
    int cellCount = 6;
    for (int i = 0; i<cellCount; i++) {
        CAEmitterCell * cell = [self getEmitterCellAction];
        [mArr addObject:cell];
    }
    [self emiterLayer].emitterCells = mArr; // 将粒子组成的数组赋值给CAEmitterLayer的emitterCells特色即可.
}
- (CAEmitterCell *)getEmitterCellAction {
    CAEmitterCell *cell = [[CAEmitterCell alloc] init];
    //    cell.contents = (__bridge id)[UIImage imageNamed:@"coin"].CGImage; // 粒子中的图片
    cell.contents = (__bridge id _Nullable)([self setRandomColorCircleImageSize:CGSizeMake(20, 20)].CGImage);
    cell.yAcceleration = arc4random_uniform(80);   // 粒子的初始加快度
    cell.xAcceleration = -cell.yAcceleration-10;
    cell.birthRate = 10.f;           // 每秒生成粒子的个数
    cell.lifetime = 6.f;            // 粒子存活时刻
    cell.alphaSpeed = -0.1f;        // 粒子消逝的速度
    cell.velocity = 30.f;           // 粒子运动的速度均值
    cell.velocityRange = 100.f;      // 粒子运动的速度扰动规模
    cell.emissionRange = M_PI; // 粒子发射角度, 这儿是一个扇形.
//    cell.scale = 0.2;
//    cell.scaleRange = 0.1;
//    cell.scaleSpeed = 0.02;
    CGFloat colorChangeValue  = 50.0f;
    cell.blueRange = colorChangeValue;
    cell.redRange =  colorChangeValue;
    cell.greenRange =  colorChangeValue;
    return cell;
}

emitterShape发射源形状取值不同时会有不同作用。

  • kCAEmitterLayerPoint:
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCAEmitterLayerLine: 线
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

7.8 CATiledLayer|CALayer

7.8.1 惯例加载图片的做法

  • 有些时分咱们或许需求制造一个很大的图片,常见的比方便是一个高像素的照片或许是地球外表的具体地图
  • iOS运用通畅运转在内存受限的设备上,所以读取整个图片到内存中是不明智的。
  • 载入大图或许会相当地慢,那些对你看上去比较便利的做法(在主线程调用UIImage-imageNamed:办法或许-imageWithContentsOfFile:办法)将会堵塞你的用户界面,至少会引起动画卡顿现象。
  • 能高效制造在iOS上的图片也有一个巨细约束。一切显现在屏幕上的图片终究都会被转化为OpenGL纹路,同时OpenGL有一个最大的纹路尺度(一般是2048*2048,或 4096*4096,这个取决于设备类型)。
    • 假如你想在单个纹路中显现一个比这大的图,即便图片现已存在于内存中了,你仍然会遇到很大的功能问题,因为Core Animation强制用CPU处理图片而不是更快的GPU
  • CATiledLayer为载入大图形成的功能问题供给了一个解决方案:将大图分解成小片然后将他们单独按需载入

7.8.2 CATiledLayer加载大图

CATiledLayerCore Animation 结构中的一个特别的 CALayer 子类,用于有用地显现大图或许高分辨率的内容
它的作用是将大图切割成小块只在需求时才加载和显现这些小块,以进步功能和内存功率。

原理和作业机制

  • 切割大图:
    CATiledLayer 会将一个大的图片或许内容切割成多个小的切片(tiles)。
  • 动态加载:
    当用户浏览大图时,CATiledLayer 会动态地加载并显现用户所需求的切片,而不是一次性加载整张图片。
  • 显现优化:
    只要在需求时,CATiledLayer 才会加载和烘托切片,因而它能够在处理大尺度图片或许高分辨率内容时,坚持较低的内存占用和较好的功能体现。
  • 多线程处理:
    CATiledLayer 运用多线程机制来处理切片的加载和烘托,以进步用户体会和全体功能。

运用办法

  • 创立 CATiledLayer:
    经过创立 CATiledLayer 目标并将其增加到需求显现大图的视图中。
  • 设置署理:
    CATiledLayer 的署理目标需求完结 drawLayer:inContext: 办法来制造每个切片。
  • 指定分辨率和缩放等级:
    设置 CATiledLayerlevelsOfDetaillevelsOfDetailBias 特色来操控切片的分辨率显现优先级
  • 完结署理办法:
    完结 drawLayer:inContext: 办法,依据给定的 rectcontext 制造对应切片的内容。

7.8.3 CATiledLayer的三个重要特色

  • CATiledLayer将需求制造的内容切割成许多小块,然后在许多线程里按需异步制造相应的小块,具体怎么区分小块和缩放时的加载战略与CATiledLayer三个重要特色有关:
    • levelsOfDetail
      • 作用: levelsOfDetail 特色用于指定 CATiledLayer 的等级(levels)的数量,即分辨率等级。这决议了在不同缩放等级下加载的切片数量。
      • 类型: Int 类型,表明等级的数量。
      • 默许值: 默许值为 1。
      • 运用场景:
        • 假如设置为 1,表明只要一个分辨率等级,一切缩放等级下加载的切片都是相同的分辨率。
        • 假如设置为较大的值,表明在不同缩放等级下会加载不同分辨率的切片,以进步显现作用和功能。
    • levelsOfDetailBias
      • 作用: levelsOfDetailBias 特色用于指定 CATiledLayer 在挑选加载切片时的偏好等级(bias level)。它决议了在缩放时优先加载哪个分辨率等级的切片。
      • 类型: Int 类型,表明偏好等级的数量。
      • 默许值: 默许值为 0。
      • 运用场景:
        • 设置为较大的正数时,会倾向于加载较高分辨率的切片,然后进步显现质量。
        • 设置为负数时,则倾向于加载低分辨率的切片,以进步功能。
    • tileSize
      • 作用: tileSize 特色用于指定每个切片的尺度。切片是 CATiledLayer 内部用于加载和显现的基本单位。
      • 类型: CGSize 类型,表明切片的尺度。
      • 默许值: 默许值为 (256, 256)。
      • 运用场景:
        • 能够依据具体的需求和功能要求来调整切片的尺度。较大的切片尺度或许会进步加载功率,但也会增加内存占用和烘托担负。
        • 一般情况下,建议将切片尺度设置为较小的值,以便在加载和显现时能够更好地操控内存运用和功能。

代码示例:

中心代码:

#import "TileImageView.h"
@implementation TileImageView{
    UIImage *originImage;
    CGRect imageRect;
    CGFloat imageScale;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
    // Drawing code
}
*/
+(Class)layerClass{
    return [CATiledLayer class];
}
-(id)initWithImageName:(NSString*)imageName andFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if(self){
        self.tileCount = 36;
        self.imageName = imageName;
        [self initSelf];
    }
    return self;
}
-(id)initWithImageName:(NSString *)imageName andFrame:(CGRect)frame andTileCount:(NSInteger)tileCount{
    self = [self initWithFrame:frame];
    if(self){
        self.tileCount = tileCount;
        self.imageName = imageName;
        [self initSelf];
    }
    return self;
}
-(void)initSelf{
    NSString *path = [[NSBundle mainBundle]pathForResource:[_imageName stringByDeletingPathExtension] ofType:[_imageName pathExtension]];
    originImage = [UIImage imageWithContentsOfFile:path];
    imageRect = CGRectMake(0.0f, 0.0f,CGImageGetWidth(originImage.CGImage),CGImageGetHeight(originImage.CGImage));
    imageScale = self.frame.size.width/imageRect.size.width;
    CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
     //依据图片的缩放核算scrollview的缩放次数
     // 图片相关于视图扩大了1/imageScale倍,所以用log2(1/imageScale)得出缩放次数,
     // 然后经过pow得出缩放倍数,至于为什么要加1,
     // 是期望图片在扩大到原图份额时,还能够继续扩大一次(即2倍),能够看的更明晰
    int lev = ceil(log2(1/imageScale))+1;
    tiledLayer.levelsOfDetail = 1;
    tiledLayer.levelsOfDetailBias = lev;
    if(self.tileCount>0){
        NSInteger tileSizeScale = sqrt(self.tileCount)/2;
        CGSize tileSize = self.bounds.size;
        tileSize.width /=tileSizeScale;
        tileSize.height/=tileSizeScale;
        tiledLayer.tileSize = tileSize;
    }
}
-(void)setFrame:(CGRect)frame{
    [super setFrame:frame];
    imageScale = self.frame.size.width/imageRect.size.width;
    if(self.tileCount>0){
        CATiledLayer *tileLayer = (CATiledLayer *)self.layer;
        CGSize tileSize = self.bounds.size;
        NSInteger tileSizeScale = sqrt(self.tileCount)/2;
        tileSize.width /=tileSizeScale;
        tileSize.height/=tileSizeScale;
        tileLayer.tileSize = tileSize;
    }
}
-(CGPoint)rectCenter:(CGRect)rect{
    CGFloat centerX = (CGRectGetMaxX(rect)+CGRectGetMinX(rect))/2;
    CGFloat centerY = (CGRectGetMaxY(rect)+CGRectGetMinY(rect))/2;
    return CGPointMake(centerX, centerY);
}
-(void)drawRect:(CGRect)rect {
    //将视图frame映射到实践图片的frame
    CGRect imageCutRect = CGRectMake(rect.origin.x / imageScale,
                                     rect.origin.y / imageScale,
                                     rect.size.width / imageScale,
                                     rect.size.height / imageScale);
    //截取指定图片区域,重绘
    @autoreleasepool{
        CGImageRef imageRef = CGImageCreateWithImageInRect(originImage.CGImage, imageCutRect);
        UIImage *tileImage = [UIImage imageWithCGImage:imageRef];
        CGContextRef context = UIGraphicsGetCurrentContext();
        UIGraphicsPushContext(context);
        [tileImage drawInRect:rect];
        UIGraphicsPopContext();
    }
    static NSInteger drawCount = 1;
    drawCount ++;
    if(drawCount == self.tileCount){
    }
}
-(CGSize)returnTileSize{
    return [(CATiledLayer*)self.layer tileSize];
}
@end

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
具体大图,能够依据自己的大图进行设置

7.9 CAEAGLLayer|CALayer

CAEAGLLayerCore Animation 结构中的一个特别类型的 CALayer 子类,用于在 iOS 和 macOS 上显现 OpenGL ES 烘托内容。它供给了一个将 OpenGL ES 烘托成果直接显现在屏幕上的高效办法。下面是对 CAEAGLLayer 的具体介绍:

  • 作业原理和特色:
    • OpenGL ES 烘托: CAEAGLLayer 供给了一个用于显现 OpenGL ES 烘托成果的外表,并经过 EAGLContext 供给的 OpenGL ES 上下文来完结烘托。
    • 高效显现: 因为 CAEAGLLayer 直接与 OpenGL ES 交互,所以能够以高效的办法显现 OpenGL ES 烘托内容,避免了额定的内存拷贝和转化。
    • 跨渠道: CAEAGLLayer 能够在 iOS 和 macOS 渠道上运用,以显现相同的 OpenGL ES 烘托成果。
    • 灵活性: 经过将 CAEAGLLayer 增加到视图层次结构中,能够将 OpenGL ES 烘托内容与其他 Core Animation 图层混合在一同,完结更丰厚的用户界面作用。
  • 运用办法:
    • 创立 CAEAGLLayer 实例: 运用 init() 办法创立 CAEAGLLayer 实例,并设置其特色。
    • 创立并装备 EAGLContext: 创立一个 EAGLContext 实例,并将其与 CAEAGLLayer 相关。
    • 完结 OpenGL ES 烘托逻辑: 在 EAGLContext 中履行 OpenGL ES 烘托操作,将成果制造到 CAEAGLLayer 中。
    • 将 CAEAGLLayer 增加到视图层次结构中: 经过将 CAEAGLLayer 增加到视图层次结构中,以显现 OpenGL ES 烘托成果。

代码示例:

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <GLKit/GLKit.h>
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *glView;
@property (nonatomic, strong) EAGLContext *glContext;
@property (nonatomic, strong) CAEAGLLayer *glLayer;
@property (nonatomic, assign) GLuint framebuffer;
@property (nonatomic, assign) GLuint colorRenderbuffer;
@property (nonatomic, assign) GLint framebufferWidth;
@property (nonatomic, assign) GLint framebufferHeight;
@property (nonatomic, strong) GLKBaseEffect *effect;

@end
@implementation ViewController
- (void)setUpBuffers
{
    //set up frame buffer
    glGenFramebuffers(1, &_framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
    //set up color render buffer
    glGenRenderbuffers(1, &_colorRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
    [self.glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.glLayer];
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_framebufferWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_framebufferHeight);
    //check success
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"Failed to make complete framebuffer object: %i", glCheckFramebufferStatus(GL_FRAMEBUFFER));
    }
}
- (void)tearDownBuffers
{
    if (_framebuffer) {
        //delete framebuffer
        glDeleteFramebuffers(1, &_framebuffer);
        _framebuffer = 0;
    }
    if (_colorRenderbuffer) {
        //delete color render buffer
        glDeleteRenderbuffers(1, &_colorRenderbuffer);
        _colorRenderbuffer = 0;
    }
}
- (void)drawFrame {
    //bind framebuffer & set viewport
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
    glViewport(0, 0, _framebufferWidth, _framebufferHeight);
    //bind shader program
    [self.effect prepareToDraw];
    //clear the screen
    glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0);
    //set up vertices
    GLfloat vertices[] = {
        -0.5f, -0.5f, -1.0f, 0.0f, 0.5f, -1.0f, 0.5f, -0.5f, -1.0f,
    };
    //set up colors
    GLfloat colors[] = {
        0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
    };
    //draw triangle
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(GLKVertexAttribColor);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(GLKVertexAttribColor,4, GL_FLOAT, GL_FALSE, 0, colors);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    //present render buffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER];
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    //set up context
    self.glContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:self.glContext];
    //set up layer
    self.glLayer = [CAEAGLLayer layer];
    self.glLayer.frame = self.glView.bounds;
    [self.glView.layer addSublayer:self.glLayer];
    self.glLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking:@NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8};
    //set up base effect
    self.effect = [[GLKBaseEffect alloc] init];
    //set up buffers
    [self setUpBuffers];
    //draw frame
    [self drawFrame];
}
- (void)viewDidUnload
{
    [self tearDownBuffers];
    [super viewDidUnload];
}
- (void)dealloc
{
    [self tearDownBuffers];
    [EAGLContext setCurrentContext:nil];
}
@end

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
在一个实在的OpenGL运用中,咱们或许会用 NSTimerCADisplayLink 周期性地每秒钟调用 -drawRect 办法60次,同时会将几许图形生成和制造分开以便不会每次都从头生成三角形的极点(这样也能够让咱们制造其他的一些东西而不是一个三角形罢了),不过上面这个比方现已满意演示了绘图准则了

三、CoreAnimation中心动画

Core Animation 供给高效地动画能力,咱们先依照派生联系的办法来了解一下动画相关类:

1. 动画相关类介绍

派生联系如图所示:

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

1. CAAnimation

CAAnimation是中心动画的基类

  • 不能直接运用,首要担任动画的时刻速度
  • 自身完结了CAMediaTiming协议。
@interface CAAnimation : NSObject
    <NSSecureCoding, NSCopying, CAMediaTiming, CAAction>
{
@private
  void *_attr;
  uint32_t _flags;
}
@property(nullable, strong) id <CAAnimationDelegate> delegate;
CAAnimation特色 阐明
timingFunction CAMediaTimingFunction速度操控函数,操控动画运转的节奏
removedOnCompletion 默许为YES,代表动画履行完毕后就从图层上移除,图形会康复到动画履行前的状况。假如想让图层坚持显现动画履行后的状况,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
delegate 署理(animationDidStartanimationDidStop

ps:CAMediaTimingFunction介绍

kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感觉
kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加快离开
kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的抵达目的地
kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中心加快,然后减速的抵达目的地。这个是默许的动画行为。

1.1 CAMediaTiming协议

duration,beginTime、repeatCount、speed、timeOffset、repeatDuration、autoreverses这些时刻相关的特色都在这个类中。协议中的这些特色经过一些办法结合在一同,准确的操控着时刻。

CAMediaTiming特色 阐明
beginTime 指定动画开端的时刻。从开端推迟几秒的话,设置为【CACurrentMediaTime() + 秒数】 的办法
duration 动画的时长
speed 动画运转速度(假如把动画的duration设置为3秒,而speed设置为2,动画将会在1.5秒完毕,因为它以两倍速在履行)
timeOffset 结合一个暂停动画(speed=0)一同运用来操控动画的“当时时刻”。暂停的动画将会在第一帧卡住,然后经过改动timeOffset来随意操控动画进程
repeatCount 重复的次数。不断重复设置为 HUGE_VALF
repeatDuration 设置动画的时刻。在该时刻内动画一直履行,不计次数。
autoreverses 动画完毕时是否履行逆动画,假如duration为1s,则完结一次autoreverse就需求2s。
fillMode CAMediaTimingFillMode枚举

ps:CAMediaTimingFillMode介绍

kCAFillModeRemoved:这个是默许值,也便是说当动画开端前和动画完毕后,动画对layer都没有影响,动画完毕后,layer会康复到之前的状况
kCAFillModeForwards:当动画完毕后,layer会一直坚持着toValue的状况
kCAFillModeBackwards:假如要让动画在开端之前(推迟的这段时刻内)显现fromValue的状况
kCAFillModeBoth:这个其实便是上面两个的组成.动画加入后开端之前,layer便处于动画初始状况,动画完毕后layer坚持动画终究的状况
留意有必要配合animation.removeOnCompletion = NO才能达到以上作用

2. CAAnimationGroup|派生自CAAnimation

  • CAAnimation的子类
  • 单一的动画并不能满意某些特定需求,这时就需求用到CAAnimationGroup
  • 默许情况下,一组动画目标是同时运转的,也能够经过设置动画目标的beginTime特色来更改动画的时刻
CATransition特色 阐明
animations [CAAnimation],动画组

代码如下

let groupAnim = CAAnimationGroup()
//创立keyAnim
let keyAnim = CAKeyframeAnimation(keyPath: "position")
//设置values
keyAnim.values = [NSValue(cgPoint: CGPoint(x: 100, y: 200)),
                  NSValue(cgPoint: CGPoint(x: 200, y: 200)),
                  NSValue(cgPoint: CGPoint(x: 200, y: 300)),
                  NSValue(cgPoint: CGPoint(x: 100, y: 300)),
                  NSValue(cgPoint: CGPoint(x: 100, y: 400)),
                  NSValue(cgPoint: CGPoint(x: 200, y: 500))]
keyAnim.duration = 4.0
keyAnim.timingFunctions = [CAMediaTimingFunction(name: .easeInEaseOut)]
//创立突变圆角
let animation = CABasicAnimation(keyPath: "cornerRadius")
animation.toValue = 40
animation.duration = 4.0
imgView?.layer.masksToBounds = true
groupAnim.animations = [keyAnim, animation]
groupAnim.duration = 4.0
groupAnim.repeatCount = MAXFLOAT
groupAnim.autoreverses = true
imgView?.layer.add(groupAnim, forKey: "groupAnim")

将动画分组在一同的更高级办法是运用业务目标(CATransaction业务类)。经过答应您创立嵌套的动画集并为每个动画分配不同的动画参数,业务供给了更大的灵活性。

3. CATransition|派生自CAAnimation

CATransition头文件

  • 动画特色:
    • type:动画过渡类型
    • subtype:动画过渡方向
    • startProgress:动画起点(在全体动画的百分比)
    • endProgress:动画结尾(在全体动画的百分比)
    • …….
@interface CATransition : CAAnimation
/* The name of the transition. Current legal transition types include
 * `fade', `moveIn', `push' and `reveal'. Defaults to `fade'. */
@property(copy) NSString *type;
/* An optional subtype for the transition. E.g. used to specify the
 * transition direction for motion-based transitions, in which case
 * the legal values are `fromLeft', `fromRight', `fromTop' and
 * `fromBottom'. */
@property(copy) NSString *subtype;
/* The amount of progress through to the transition at which to begin
 * and end execution. Legal values are numbers in the range [0,1].
 * `endProgress' must be greater than or equal to `startProgress'.
 * Default values are 0 and 1 respectively. */
@property float startProgress;
@property float endProgress;
/* An optional filter object implementing the transition. When set the
 * `type' and `subtype' properties are ignored. The filter must
 * implement `inputImage', `inputTargetImage' and `inputTime' input
 * keys, and the `outputImage' output key. Optionally it may support
 * the `inputExtent' key, which will be set to a rectangle describing
 * the region in which the transition should run. Defaults to nil. */
@property(nullable, strong) id filter;
@end
/* Common transition types. */
CA_EXTERN CATransitionType const kCATransitionFade
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CATransitionType const kCATransitionMoveIn
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CATransitionType const kCATransitionPush
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CATransitionType const kCATransitionReveal
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
/* Common transition subtypes. */
CA_EXTERN CATransitionSubtype const kCATransitionFromRight
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CATransitionSubtype const kCATransitionFromLeft
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CATransitionSubtype const kCATransitionFromTop
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CA_EXTERN CATransitionSubtype const kCATransitionFromBottom
    API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
/** Animation subclass for grouped animations. **/

转场动画过渡作用

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

CATransition *anim = [CATransition animation];
// 转场类型
anim.type = @"cube";
// 动画履行时刻
anim.duration = 0.5;
// 动画履行方向
anim.subtype = kCATransitionFromLeft;
// 增加到View的layer
[self.redView.layer addAnimation:anim forKey];

示例Demo:


#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageV;
@end
@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.imageV.userInteractionEnabled = YES;
    //增加手势
    UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
    leftSwipe.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.imageV addGestureRecognizer:leftSwipe];
    UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipe:)];
    rightSwipe.direction = UISwipeGestureRecognizerDirectionRight;
    [self.imageV addGestureRecognizer:rightSwipe];
}
static int _imageIndex = 0;
- (void)swipe:(UISwipeGestureRecognizer *)swipe {
    //转场代码与转场动画有必要得在同一个办法傍边.
    NSString *dir = nil;
    if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
        _imageIndex++;
        if (_imageIndex > 4) {
            _imageIndex = 0;
        }
        NSString *imageName = [NSString stringWithFormat:@"%d",_imageIndex];
        self.imageV.image = [UIImage imageNamed:imageName];
        dir = @"fromRight";
    }else if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
        _imageIndex--;
        if (_imageIndex < 0) {
            _imageIndex = 4;
        }
        NSString *imageName = [NSString stringWithFormat:@"%d",_imageIndex];
        self.imageV.image = [UIImage imageNamed:imageName];
        dir = @"fromLeft";
    }
    //增加动画
    CATransition *anim = [CATransition animation];
    //设置转场类型
    anim.type = @"cube";
    //设置转场的方向
    anim.subtype = dir;
    anim.duration = 0.5;
    //动画从哪个点开端
    //    anim.startProgress = 0.2;
    //    anim.endProgress = 0.3;
    [self.imageV.layer addAnimation:anim forKey:nil];
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end

UIView类自带转场动画函数

  • 1、单视图

    +(void)transitionWithView:(UIView*)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void(^)(void))animations
    completion:(void(^)(BOOL finished))completion;
    
    • 参数阐明:
      • duration:动画的继续时刻
      • view:需求进行转场动画的视图
      • options:转场动画的类型
      • animations:将改动视图特色的代码放在这个block中
      • completion:动画完毕后,会主动调用这个block
  • 2、双视图

    + (void)transitionFromView:(UIView*)fromView toView:(UIView*)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options
    completion:(void(^)(BOOLfinished))completion;
    
    • 参数阐明:
      • duration:动画的继续时刻
      • options:转场动画的类型
      • animations:将改动视图特色的代码放在这个block中
      • completion:动画完毕后,会主动调用这个block
  • 转场动画运用留意点:转场代码有必要和转场动画代码写在一同,不然无效

不同 type 的动画作用:

  • kCATransitionFade
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionMoveIn
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionPush
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionReveal
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionCube
  • kCATransitionSuckEffect
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionOglFlip
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionRippleEffect
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionPageCurl
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionPageUnCurl
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionCameraIrisHollowOpen
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • kCATransitionCameraIrisHollowClose
    04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

4. CAPropertyAnimation|派生自CAAnimation

  • 承继自CAAnimation,不能直接运用
  • 要想创立动画目标,应该运用它的两个子类:CABasicAnimationCAKeyframeAnimation

You do not create instances of CAPropertyAnimation: to animate the properties of a Core Animation layer, create instance of the concrete subclasses CABasicAnimation or CAKeyframeAnimation.

CAPropertyAnimation特色 阐明
keyPath 经过指定CALayer的一个特色名称为keyPath(NSString类型),而且对CALayer的这个特色的值进行修正,达到相应的动画作用。比方,指定@“position”为keyPath,就修正CALayer的position特色的值,以达到平移的动画作用

5. CAKeyframeAnimation|派生自CAPropertyAnimation

  • CABasicAnimation是将特色从起始值更改为完毕值
  • CAKeyframeAnimation目标是答应你以线性非线性的办法设置一组目标值的动画。
  • 关键帧动画由一组目标数据值和每个值抵达的时刻组成。
    • 不光能够简略的只指定值数组时刻数组
    • 还能够依照途径进行更改图层的方位。
  • 动画目标选用您指定的关键帧,并经过在给定时刻段内从一个值插值到下一个值来构建动画。
CAKeyframeAnimation特色 阐明
values 关键帧值表明动画有必要履行的值,此特色中的值仅在path特色的值为nil时才运用。依据特色的类型,您或许需求用NSValue目标的NSNumber包装这个数组中的值。关于一些中心图形数据类型,您或许还需求将它们转化为id,然后再将它们增加到数组中。将给定的关键帧值运用于该层的时刻取决于动画时刻,由calculationMode、keyTimes和timingFunctions特色操控。关键帧之间的值是运用插值创立的,除非将核算形式设置为kcaanimation离散
path 依据点的特色的途径,关于包含CGPoint数据类型的层特色,您分配给该特色的途径目标界说了该特色在动画长度上的值。假如指定此特色的值,则忽略值特色中的任何数据
keyTimes keyTimes的值与values中的值一一对应指定关键帧在动画中的时刻点,取值规模为[0,1]。当keyTimes没有设置的时分,各个关键帧的时刻是平分的
timingFunctions 一个可选的CAMediaTimingFunction目标数组,指定每个关键帧之间的动画缓冲作用
calculationMode 关键帧间插值核算形式
rotationMode 界说沿途径动画的目标是否旋转以匹配途径切线

ps:

timingFunctions:动画缓冲作用

kCAMediaTimingFunctionLinear:线性起搏,使动画在其继续时刻内均匀地发生
kCAMediaTimingFunctionEaseIn:使一个动画开端缓慢,然后加快,随着它的进程
kCAMediaTimingFunctionEaseOut:使动画快速开端,然后缓慢地进行
kCAMediaTimingFunctionEaseInEaseOut:使动画开端缓慢,在其继续时刻的中心加快,然后在完结之前再放慢速度
kCAMediaTimingFunctionDefault:默许,保证动画的时刻与大多数体系动画的匹配

calculationMode:动画核算办法

kCAAnimationLinear:默许差值
kCAAnimationDiscrete:逐帧显现
kCAAnimationPaced:匀速 无视keyTimes和timingFunctions设置
kCAAnimationCubic:keyValue之间曲线滑润 可用 tensionValues,continuityValues,biasValues 调整
kCAAnimationCubicPaced:keyValue之间滑润差值 无视keyTimes

rotationMode:旋转办法

kCAAnimationRotateAuto:主动
kCAAnimationRotateAutoReverse:主动翻转 不设置则不旋转

代码1、用values特色

//创立动画目标
let keyAnim = CAKeyframeAnimation(keyPath: "position")
//设置values
keyAnim.values = [NSValue(cgPoint: CGPoint(x: 100, y: 200)),
                  NSValue(cgPoint: CGPoint(x: 200, y: 200)),
                  NSValue(cgPoint: CGPoint(x: 200, y: 300)),
                  NSValue(cgPoint: CGPoint(x: 100, y: 300)),
                  NSValue(cgPoint: CGPoint(x: 100, y: 400)),
                  NSValue(cgPoint: CGPoint(x: 200, y: 500))]
//重复次数 默许为1
keyAnim.repeatCount = MAXFLOAT
//设置是否原路回来 默许为false
keyAnim.autoreverses = true
//设置移动速度,越小越快
keyAnim.duration = 4.0
keyAnim.isRemovedOnCompletion = false
keyAnim.fillMode = .forwards
keyAnim.timingFunctions = [CAMediaTimingFunction(name: .easeInEaseOut)]
imgView?.layer.add(keyAnim, forKey: "keyAnim-Values")

代码2、用path特色

//创立动画目标
let keyAnim = CAKeyframeAnimation(keyPath: "position")
//创立一个CGPathRef目标,便是动画的道路
let path = CGMutablePath()
//主动沿着弧度移动
path.addEllipse(in: CGRect(x: 150, y: 200, width: 200, height: 100))
//设置开端方位
path.move(to: CGPoint(x: 100, y: 100))
//沿着直线移动
path.addLine(to: CGPoint(x: 200, y: 100))
path.addLine(to: CGPoint(x: 200, y: 200))
path.addLine(to: CGPoint(x: 100, y: 200))
path.addLine(to: CGPoint(x: 100, y: 300))
path.addLine(to: CGPoint(x: 200, y: 400))
//沿着曲线移动
path.addCurve(to: CGPoint(x: 50.0, y: 275.0), control1: CGPoint(x: 150.0, y: 275.0), control2: CGPoint(x: 70.0, y: 120.0))
path.addCurve(to: CGPoint(x: 150.0, y: 275.0), control1: CGPoint(x: 250.0, y: 275.0), control2: CGPoint(x: 90.0, y: 120.0))
path.addCurve(to: CGPoint(x: 250.0, y: 275.0), control1: CGPoint(x: 350.0, y: 275.0), control2: CGPoint(x: 110, y: 120.0))
path.addCurve(to: CGPoint(x: 350.0, y: 275.0), control1: CGPoint(x: 450.0, y: 275.0), control2: CGPoint(x: 130, y: 120.0))
keyAnim.path = path
//重复次数 默许为1
keyAnim.repeatCount = MAXFLOAT
//设置是否原路回来 默许为false
keyAnim.autoreverses = true
//设置移动速度,越小越快
keyAnim.duration = 4.0
keyAnim.isRemovedOnCompletion = false
keyAnim.fillMode = .forwards
keyAnim.timingFunctions = [CAMediaTimingFunction(name: .easeInEaseOut)]
imgView?.layer.add(keyAnim, forKey: "keyAnim-Path")

7. CABasicAnimation|派生自CAPropertyAnimation

  • CABasicAnimation是中心动画类簇中的一个类
    • 其父类是CAPropertyAnimation
    • 其子类是CASpringAnimation
    • 它的祖父是CAAnimation。
  • 它首要用于制造比较单一的动画,例如,平移缩放旋转色彩突变边框的值的改动等,也便是将layer的某个特色值从一个值到另一个值的改动
CABasicAnimation特色 阐明
fromValue 所改动特色的起始值
toValue 所改动特色的完毕时的值
byValue 所改动特色相同起始值的改动量

代码如下

let baseAnim = CABasicAnimation(keyPath: "position")
baseAnim.duration = 2;
//开端的方位
baseAnim.fromValue = NSValue(cgPoint: (imgView?.layer.position)!)
baseAnim.toValue = NSValue(cgPoint: CGPoint(x: 260, y: 260))
//        baseAnim.isRemovedOnCompletion = false
//        baseAnim.fillMode = CAMediaTimingFillMode.forwards
imgView?.layer.add(baseAnim, forKey: "baseAnim-position")
imgView?.center = CGPoint(x: 260, y: 260)

7.1 避免动画完毕后回到初始状况

如上面代码所示,需求增加imgView?.center = CGPoint(x: 260, y: 260)来避免避免动画完毕后回到初始状况,网上还有别的一种办法是 设置removedOnCompletion、fillMode两个特色

baseAnim.removedOnCompletion = NO;
baseAnim.fillMode = kCAFillModeForwards;

可是这种办法会形成modelLayer没有修正,_view1的实践坐标点并没有在所看到的方位,会发生一些问题

7.2 CALayer动画运转的原理

CALayer有两个实例办法presentationLayer(简称P)和 modelLayer(简称M),

/* presentationLayer
 * 回来一个layer的拷贝,假如有任何活动动画时,包含当时状况的一切layer特色
 * 实践上是迫临当时状况的近似值。
 * 测验以任何办法修正回来的成果都是未界说的。
 * 回来值的sublayers 、mask、superlayer是当时layer的这些特色的presentationLayer
 */
- (nullable instancetype)presentationLayer;
/* modelLayer
 * 对presentationLayer调用,回来当时模型值。
 * 对非presentationLayer调用,回来自身。
 * 在生成表明层的业务完结后调用此办法的成果未界说。
 */
- (instancetype)modelLayer;

从中能够看到P即是咱们看到的屏幕上展现的状况,而M便是咱们设置完立即收效的实在状况;打一个比方的话,P是个瞎子,只担任走路(制造内容),而M是个瘸子,只担任看路(怎么制造)

CALayer动画运转的原理:

  • P会在每次屏幕改写时更新状况
    • 当有动画CAAnimation(简称A)加入时,P由动画A操控进行制造,
    • 当动画A完毕被移除时P则再去取M的状况展现。
  • 可是因为M没有改动,所以动画履行完毕又会回到起点。
  • 假如想要P在动画完毕后就停在当时状况而不回到M的状况,咱们就需求给A设置两个特色:
    • 一个是A.removedOnCompletion = NO,表明动画完毕后A仍然影响着P;
    • 另一个是A.fillMode = kCAFillModeForwards;
    • 这两行代码将会让A操控住P在动画完毕后坚持不变
  • 可是此刻咱们的P和M不同步,咱们看到的P是toValue的状况,而M则仍是自己原来的状况。举个比方:
    • 咱们初始化一个view,它的状况为1,咱们给它的layer加个动画,from是0,to是2,设置fillMode为kCAFillModeForewards,则动画完毕后P的状况是2,M的状况是1,这或许会导致一些问题呈现。比方
      • 你点P所在的方位点不动,因为呼应点击的是M。所以咱们应该让P和M同步,如上代码imgView?.center = CGPoint(x: 260, y: 260)需求提一点的是:对M赋值,不会影响P的显现,当P想要显现的时分,它现已被A操控了,并不会先闪现一下。

7.3 Animation-KeyPath值

上面的动画的KeyPath值咱们只运用了position,其实还有许多类型能够设置,下面咱们列出了一些比较常用的

keyPath值 阐明 值类型
position 移动方位 CGPoint
opacity 通明度 0-1
bounds 变大与方位 CGRect
bounds.size 由小变大 CGSize
backgroundColor 布景色彩 CGColor
cornerRadius 突变圆角 恣意数值
borderWidth 改动边框border的巨细((图形周围边框,border默许为黑色)) 恣意数值
contents 改动layer内容(图片)留意假如想要达到改动内容的动画作用;首要在运转动画之前界说好layer的contents contents CGImage
transform.scale 缩放、扩大 0.0-1.0
transform.rotation.x 旋转动画(翻转,沿着X轴) M_PI*n
transform.rotation.Y 旋转动画(翻转,沿着Y轴) M_PI*n
transform.rotation.Z 旋转动画(翻转,沿着Z轴) M_PI*n
transform.translation.x 旋转动画(翻转,沿着X轴) 恣意数值
transform.translation.y 旋转动画(翻转,沿着Y轴) 恣意数值

8. 检测动画的完毕

中心动画支撑检测动画开端或完毕的时刻。这些告诉是进行与动画相关的任何内务处理使命的好时机。
例如,您能够运用开端告诉来设置一些相关的状况信息,并运用相应的完毕告诉来撤除该状况。

有两种不同的办法能够告诉动画的状况:

  • 运用setCompletionBlock:办法将完结块增加到当时业务。当业务中的一切动画完结后,业务将履行完结块。
  • 将托付分配给CAAnimation目标并完结animationDidStart:animationDidStop:finished:托付办法。

运用beginTime特色

  • 假如要让两个动画链接在一同,以便在另一个完结时启动,请不要运用动画告诉。
  • 而是运用动画目标的beginTime特色依照所需的时刻启动每个动画目标。
  • 将两个动画链接在一同,只需将第二个动画的开端时刻设置为第一个动画的完毕时刻。

每个图层都有自己的本地时刻,用于办理动画计时。一般,两个不同层的本地时刻满意挨近,您能够为每个层指定相同的时刻值,用户或许不会留意到任何内容。可是因为superLayer或其自身Layer的时序参数设置,层的本地时刻会发生改动。例如,更改Layer的speed特色会导致该Layer(及其子Layer)上的动画继续时刻按份额更改。

为了保证Layer的时刻值适宜,CALayer类界说了convertTime:fromLayer:convertTime:toLayer:办法。咱们能够运用这些办法将固定时刻值转化为Layer的本地时刻或将时刻值从一个Layer转化为另一个Layer。这些办法或许影响图层本地时刻的媒体计时特色,并回来可与其他图层一同运用的值。

可运用下面示例来获取图层的当时本地时刻。CACurrentMediaTime函数回来核算机的当时时钟时刻,该办法将本机时刻并转化为图层的本地时刻。

获取图层的当时本地时刻

CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime()fromLayer:nil];

在图层的本地时刻中有时刻值后,能够运用该值更新动画目标或图层的与时序相关的特色。运用这些计时特色,您能够完结一些风趣的动画行为,包含:

  • beginTime特色设置动画的开端时刻
    • 一般动画开端下一个周期的时分,咱们能够运用beginTime将动画开端时刻推迟几秒钟。
    • 将两个动画链接在一同的办法是将一个动画的开端时刻设置为与另一个动画的完毕时刻相匹配。
    • 假如推迟动画的开端,则或许还需求将fillMode特色设置为kCAFillModeBackwards
    • 即便图层树中的图层目标包含不同的值,此填充形式也会使图层显现动画的起始值。
    • 假如没有此填充形式,您将看到在动画开端履行之前跳转到终究值。其他填充形式也可用。
  • autoreverses特色使动画在指定时刻内履行,然后回来到动画的起始值。
    • 咱们能够将autoreverses与repeatCount组合运用,就能够起始值和完毕值之间来回动画。
    • 将重复计数设置为主动反转动画的整数(例如1.0)会导致动画中止在其起始值上。
    • 增加额定的半步(例如重复计数为1.5)会导致动画中止在其完毕值上。
    • 运用timeOffset具有组动画的特色能够在稍后的时刻启动某些动画。

9. 暂停和康复图层的动画

/**
 layer 暂停动画
 */
- (void)pauseLayer:(CALayer*)layer {
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
}
/**
 layer 继续动画
 */
- (void)resumeLayer:(CALayer*)layer {
    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer.beginTime = timeSincePause;
}

2. 动画实战

因为篇幅太大,把动画相关的总结放置鄙人一篇文章:iOS 多媒体技能| Core Animation关键回忆2【iOS动画】

四、CoreAnimation图层烘托原理

咱们在探究 iOS图层烘托原理,关于这部分,现已做过翔实阐述,在这儿咱们直接概括定论:

1. CALayer显现可视化内容的原理

为什么 CALayer 能够呈现可视化内容呢?

  • CALayer 基本等同于一个 纹路。纹路是 GPU 进行图画烘托的重要依据。
    • 核算机图形烘托原理 中说到纹路本质上便是一张图片
    • 因而 CALayer 也包含一个 contents 特色指向一块缓存区,称为 backing store,能够寄存位图(Bitmap)。iOS 中将该缓存区保存的图片称为 寄宿图
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • 图形烘托流水线
    • 支撑从极点开端进行制造(在流水线中,极点会被处理生成纹路)
    • 也支撑直接运用纹路(图片)进行烘托。
    • 相应地,在实践开发中,制造界面也有两种办法:
      • 一种是 手动制造;
      • 另一种是 运用图片
    • 对此,iOS 中也有两种相应的完结办法:
      • 运用图片:contents image
      • 手动制造:custom drawing

2. Contents Image

  • Contents Image 是指经过 CALayer 的 contents 特色来装备图片
    • contents 特色的类型为 id。在这种情况下
    • 假如 content 的值不是 CGImage ,得到的图层将是空白的。
      • 在 Mac OS 体系中,该特色对 CGImage 和 NSImage 类型的值都起作用
      • 在 iOS 体系中,该特色只对 CGImage 起作用
  • 本质上,contents 特色指向的一块缓存区域,称为 backing store,能够寄存 bitmap 数据。

3. Custom Drawing

  • Custom Drawing 是指运用 Core Graphics 直接制造寄宿图
    • 实践开发中,一般经过承继 UIView 并完结 - drawRect: 办法来自界说制造。
    • 虽然 -drawRect: 是一个 UIView 办法,但事实上都是底层的 CALayer 完结了重绘作业并保存了发生的图片。
    • 下图所示为 -drawRect: 制造界说寄宿图的基本原理
      04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】
  • UIView 有一个相关图层,即 CALayer
    • CALayer 有一个可选的 delegate 特色,完结了 CALayerDelegate 协议。UIView 作为 CALayer 的署理完结了 CALayerDelegae 协议。
    • 当需求重绘时,即调用 -drawRect:,CALayer 请求其署理给予一个寄宿图来显现。
    • CALayer 首要会测验调用 -displayLayer: 办法,此刻署理能够直接设置 contents 特色
      - (void)displayLayer:(CALayer *)layer;
      
    • 假如署理没有完结 -displayLayer: 办法,CALayer 则会测验调用 -drawLayer:inContext: 办法。
      • 在调用该办法前,CALayer 会创立一个空的寄宿图(尺度由 bounds 和 contentScale 决议)和一个 Core Graphics 的制造上下文,为制造寄宿图做准备,作为 ctx 参数传入
      - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
      
  • 终究,由 Core Graphics 制造生成的寄宿图会存入 backing store

4. Core Animation 烘托流水线

4.1 Core Animation Pipeline 烘托流水线

Core Animation 烘托流水线的作业原理

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

  • 事实上,app 自身并不担任烘托,烘托则是由一个独立的进程担任,即 Render Server 进程
  • App 经过 IPC 将烘托使命及相关数据提交给 Render Server
  • Render Server 处理完数据后,再传递至 GPU
  • 终究由 GPU 调用 iOS 的图画设备进行显现

Core Animation 流水线的具体进程

  • Handle Events: 首要,由 app 处理作业(Handle Events)
    • 如:用户的点击操作,在此进程中 app 或许需求更新 视图树,相应地,图层树 也会被更新;
  • Commit Transaction: 其次,app 经过 CPU 完结对显现内容的核算
    • 如:视图的创立、布局核算、图片解码、文本制造等。在完结对显现内容的核算之后,app 对图层进行打包,并鄙人一次 RunLoop 时将其发送至 Render Server,即完结了一次 Commit Transaction 操作;
  • Render Server: Render Server 首要履行 Open GL/Metal、Core Graphics 相关程序,并调用 GPU;
    • Decode: 打包好的图层被传输到 Render Server 之后,首要会进行解码。留意完结解码之后需求等待下一个 RunLoop 才会履行下一步 Draw Calls
    • Draw Calls: 解码完结后,Core Animation 会调用基层烘托结构(比方 OpenGL 或许 Metal)的办法进行制造,然后调用到 GPU
    • Render: 这一阶段首要由 GPU 进行烘托,GPU 在物理层上完结了对图画的烘托
  • Display: 显现阶段。终究,GPU 经过 Frame Buffer、视频操控器等相关部件,将图画显现在屏幕上。需求等 render 完毕的下一个 RunLoop 才触发显现;

对上述进程进行串联,它们履行所耗费的时刻远远超越 16.67 ms,因而为了满意对屏幕的 60 FPS 改写率的支撑,需求将这些进程进行分解,经过流水线的办法进行并行履行,如下图所示

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

4.2 Commit Transaction 发生了什么

一般开发傍边能影响到的便是 Handle EventsCommit Transaction 这两个阶段,这也是开发者接触最多的部分。 Handle Events 便是处理接触作业等交互作业; 在 Core Animation 流水线中,app 调用 Render Server 前的终究一步 Commit Transaction 其实能够细分为 4 个进程:

  • Layout
  • Display
  • Prepare
  • Commit

4.2.1 Layout(构建视图)

Layout 阶段首要进行视图构建和布局,具体进程包含:

  1. 调用重载的 layoutSubviews 办法
  2. 创立视图,并经过 addSubview 办法增加子视图
  3. 核算视图布局,即一切的 Layout Constraint

因为这个阶段是在 CPU 中进行,一般是 CPU 约束或许 IO 约束,所以咱们应该尽量高效轻量地操作,削减这部分的时刻。比方削减非必要的视图创立、``简化布局核算削减视图层级等。

4.2.2 Display(制造视图)

  • 这个阶段首要是交给 Core Graphics 进行视图的制造,留意不是实在的显现,而是得到前文所说的图元 primitives 数据
    • 依据上一阶段 Layout 的成果创立得到图元信息。
    • 假如重写了 drawRect: 办法,那么会调用重载的 drawRect: 办法,在 drawRect: 办法中手动制造得到 bitmap 数据,然后自界说视图的制造
  • 留意正常情况下 Display 阶段只会得到图元 primitives 信息,而位图 bitmap 是在 GPU 中依据图元信息制造得到的
  • 可是假如重写了 drawRect: 办法,这个办法会直接调用 Core Graphics 制造办法得到 bitmap 数据,同时体系会额定申请一块内存,用于暂存制造好的 bitmap;
  • 因为重写了 drawRect: 办法,导致制造进程从 GPU 转移到了 CPU,这就导致了一定的功率丢失
  • 与此同时,这个进程会额定运用 CPU 和内存,因而需求高效制造,不然简单形成 CPU 卡顿或许内存爆破

4.2.3 Prepare(Core Animation 额定的作业)

Prepare 阶段归于附加进程,一般处理图画的解码和转化等操作

  • 关于图画的 解码烘托,以及相关的优化,能够学习文章:
  • 1

4.2.4 Commit(打包并发送)

  • 这一步首要是:将图层打包并发送到 Render Server
  • 留意 commit 操作是 依靠图层树递归履行 的,所以假如图层树过于复杂,commit 的开支就会很大
  • 这也是咱们期望削减视图层级,然后下降图层树复杂度的原因

4.3 Rendering Pass: Render Server 的具体操作

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

04-iOS 多媒体技能| Core Animation关键回忆1【CAlayer与UIView树、中心动画、Core Animation烘托原理等】

Render Server 一般是 OpenGL 或许是 Metal。以 OpenGL 为例,那么上图首要是 GPU 中履行的操作,具体首要包含:

  • GPU 收到 Command Buffer,包含图元 primitives 信息
  • Tiler 开端作业:先经过极点着色器 Vertex Shader 对极点进行处理,更新图元信息
  • 平铺进程:平铺生成 tile bucket 的几许图形,这一步会将图元信息转化为像素,之后将成果写入 Parameter Buffer 中
  • Tiler 更新完一切的图元信息,或许 Parameter Buffer 已满,则会开端下一步
  • Renderer 作业:将像素信息进行处理得到 bitmap,之后存入 Render Buffer
  • Render Buffer 中存储有烘托好的 bitmap,供之后的 Display 操作运用

参阅与引荐