前言
咱们在前面,首要进行了针对 iOS中的多媒体技能相关几个结构概述:
- 然后 用 两篇文章 对 其间的
UIKit
相关关键 进行了分述:- 咱们 在此篇文章 ,将 针对 Core Animation结构的关键 进一步打开分述:
一、Core Animation简介
1. 官方介绍
依照惯例,咱们首要引进苹果官方文档对其的介绍:
Render, compose, and animate visual elements.
OverViewCore 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
中心关键
咱们翻开 中心动画的 编程指南 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
以及许多其他能够修正的面向视觉的特色。 - 更改特色值会导致创立隐式动画
- 与View相同,图层目标也有一个鸿沟矩形、屏幕上的
-
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
制造内容进程:
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 特色进行了设置:
- 而当设备屏幕进行改写时,会从 CALayer 中读取生成好的
- 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.
- contents 供给了 layer 的内容,是一个指针类型
- 那么在运转时,操作体系会调用底层的接口,将 image 经过 CPU+GPU 的烘托流水线烘托得到对应的 bitmap,存储于 CALayer.contents 中,在设备屏幕进行改写的时分就会读取 bitmap 在屏幕上呈现。
- 也正因为每次要被烘托的内容是被静态的存储起来的,所以每次烘托时,Core Animation 会触发调用 drawRect: 办法,运用存储好的 bitmap 进行新一轮的展现
1.1.2 依据layer的动画
-
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
1.2 layer目标界说自己的几许形状
视觉几许包含有关该内容的border
、bounds
、position
、transform
(旋转、缩放或改换)、shadow
等
1.2.1 两种类型的坐标系
运用layer
目标开发进程中,咱们会涉及到两套坐标系: 点坐标系, 单位坐标系。 其间点坐标系和咱们在用 UIKIt中的view
开发时,相差无几
1. 点坐标系
- 指定
layer
的巨细和方位,别离运用bounds
和position
特色 - 界说
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
- 需求留意的一件事是该
position
特色坐落图层的中心
。该特色是其界说依据图层anchorPoint
特色中的值而改动的多个特色之一
2. 单位坐标系
中心动画运用单位坐标
来表明特色,这些特色的值或许会随着图层巨细的改动而改动。
- 锚点
anchorPoint
是咱们运用单位坐标系指定的多个特色之一。 - 能够将单位坐标视为指定总或许值的
百分比
- 单位坐标空间中的每个坐标的规模为0.0到1.0。例如:
- 沿 x 轴,左边际坐落坐标 处0.0,右边际坐落坐标 处1.0。
- 沿y轴,单位坐标值的方向依据渠道的不同而改动
1.2.2 锚点影响几许操作
- 运用图层的
anchorPoint
特色来访问该锚点 - 图层的几许相关操作是相关于该图层的锚点进行的
-
transform
方位特色始终是相关于图层的锚点指定的,而且运用到图层的任何改换也相关于锚点发生
修正锚点值,影响旋转操作示例:
-
anchorPoint
从(0.5,0.5)改成(0.0,0.0) - 旋转作用改动:
1.3 三组layer树
运用 Core Animation 的运用程序具有三组图层目标。每组图层目标在使运用程序的内容显现在屏幕上方面都有不同的作用:
-
model layer 树 (“layer 树)
- 运用程序交互最多的目标。
- 此树中的目标是存储任何动画的目标值的模型目标。
- 每当您更改图层的特色时,都会运用这些目标之一。
-
演示树 presentation tree
- 目标中包含任何正在运转的动画的运转中值
- 图层树目标包含动画的目标值,而演示树中的目标反映屏幕上呈现的当时值
- 内部特色可读不可写
- 能够从这些值开端创立一个新动画
-
烘托树render tree
- 目标履行实践的动画,而且是中心动画私有的
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 两个中心联系:
- CALayer 是 UIView 的特色之一,担任
烘托
和动画
,供给可视内容的呈现。 - UIView 供给了对 CALayer 部分功用的封装,同时也别的担任了
交互作业
的处理- 为什么 UIKit 中的视图能够呈现可视化内容?便是因为 UIKit 中的每一个 UI 视图控件其实内部都有一个相关的 CALayer,即 backing layer
- CALayer 事实上是用户所能在屏幕上看见的一切的根底
1.4.4 依据 两个中心联系 的拓宽
1.4.4.1 两者的异同
相同点:
-
相同的层级结构:
每个 UIView 都一一对应CALayer
担任页面的制造,所以视图层级具有视图树
的树形结构,对应 CALayer 层级也具有图层树
的树形结构- 其间,
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子类及其用处介绍
2.3 供给图层的内容|contents特色
2.3.1 设置图层内容
- 运用
contents
特色设置图层的内容,可所以CGImage
、UIImage
、UIColor
等类型 - 这个特色的类型被界说为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
}
}
运转成果如下:
2.3.2 contentGravity特色
加载的图片并不 刚好是一个方的,为了习惯这个视图,它有一点点被拉伸了。在运用UIImageView 的时分遇到过相同的问题,解决办法便是把 contentMode
特色设置成更适宜的 值,像这样:
imageView.contentMode = .scaleAspectFill
CALayer与 contentMode 对应的特色叫做 contentsGravity
self.v.layer.contentsGravity = .resizeAspect
运转成果如下:
将上面的contentGravity改一下:
self.v.layer.contentsGravity = .resizeAspectFill
运转作用如下:
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差不多:
...
@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 子图层和剪辑
- 启用剪切
layer.masksToBounds = YES
- 图层剪切蒙版的形状包含图层的角半径(假如已指定)。图 4-3显现的图层演示了该
masksToBounds
特色怎么影响具有圆角的图层。
当该特色设置为 时NO,子图层将完好显现,即便它们超出了父图层的鸿沟。更改特色会YES导致其内容被剪裁。
- 图层剪切蒙版的形状包含图层的角半径(假如已指定)。图 4-3显现的图层演示了该
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改换有用
-
-
CGAffineTransform
和CATransform3D
的异同- 与
CGAffineTransform
相似,CATransform3D
也是一个矩阵,可是和2×3的矩阵不同,CATransform3D
是一个能够在3维空间内做改换的4×4的矩阵。 - 和
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轴和这两个轴别离笔直,指向视角外为正方向。
用例1:对视图内的图层绕Y轴做45度角的旋转
CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0);
v.layer.transform = transform;
4.3.1 透视投影
-
CATransform3D
的透视作用经过一个矩阵中一个很简略的元从来操控:m34 -
m34用于按份额缩放
X
和Y
的值来核算到底要离视角多远。 - 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:
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
来制造的图层子类。- 参阅这篇文章了解:
矢量图形
和bitmap
的区别
- 参阅这篇文章了解:
- 指定比方色彩
color
和linewidth
线宽等特色,用 CGPath 来界说想要制造的图形,终究CAShapeLayer 就主动烘托出来了-
CGPath
能表明的形状,CAShapeLayer
都能够制造出来。 - 换句话说CGPath能够约束CAShapeLayer的形状。
- CAShapeLayer有一个特色Path,将途径赋值给这个特色即可。
-
- 也能够用
Core Graphics
直接向原始的CALyer
的内容中制造一个途径,相比之下,运用 CAShapeLayer 有以下一些优点:-
烘托快速:
- CAShapeLayer 运用了硬件加快,制造同一图形会比用Core Graphics快许多
-
高效运用内存:
- 一个 CAShapeLayer 不需求像一般 CALayer 相同创立一个寄宿图形,所以无论有多大,都不会占用太多的内存
-
不会被图层鸿沟剪裁掉:
- 一个 CAShapeLayer 能够在鸿沟之外制造。
- 你的图层途径不会像在运用Core Graphics的一般 CALayer 相同被剪裁掉。
-
不会呈现像素化:
- 当你给 CAShapeLayer 做3D改换时,它不像一个有寄宿图 的一般图层相同变得像素化。
-
烘托快速:
一些运用场景:
-
- 作为遮罩:
-
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的形状
- 作为遮罩时不必设置色彩特色,
-
- 动画作用:
- 经过不断的改动
CAShapeLayer
的path然后达到动画的作用 - 能够做出中心动画难以完结的作用,比方
-
粘性动画
、单边的弹性下拉作用
、qq的粘性按钮作用
、正弦波涛线
等等,相当丰厚,我这儿供给几个链接
粘性动画
-
-
- 两个特色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]; }
-
- 虚线作用:
- 虚线作用只需设置
lineDashPattern
特色:self.shapLayer.lineDashPattern = @[@(4),@(4)];
- 数组中第一个4表明先画4个点的实线,第二4表明接着距离4个点不画线
-
- 二维码扫描框: 二维码的扫描框一般是中心矩形为通明,其他边框为带通明度的黑色
//包裹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];
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; }]; } }
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代表着完毕
-
-
startPoint
和endPoint
特色- 他们决议了突变的方向。
- 这两个参数是以
单位坐标系
进行的界说,所以左上角坐标是{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) } }
事例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)
}
}
运用
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];
- 更改一下特色replicator.instanceCount = 3; 动画就不接连了
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];
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度。
- 设置
replicator.instanceCount = 3;
- 作用如下,因为方块2旋转了45度,所以方块2的上方(黑色边表明上方)也是旋转之后的上方,方块3便是沿着方块2的上方平移50点然后再旋转45度。
- 设置replicator.instanceCount = 4; 经过上面的揣度,下面的作用应该能自己想出来了。
- 这儿要特别留意CATransform3DRotate旋转改换,该方块旋转之后自己的坐标系也发生了相同角度的旋转(感觉是每个方块都有自己的坐标系),在旋转之后再要进行平移操作,那也是依照旋转之后的坐标系进行平移。 (上面的揣度纯属个人判断,如有错误还望指正!)
CATransform3D trans = CATransform3DMakeTranslation(0, -50, 0); trans = CATransform3DRotate(trans, M_PI_4, 0, 0, 1); trans = CATransform3DTranslate(trans, 21, 0, 0); //依照上面旋转之后的坐标系进行平移(当时坐标系的右方向平移21)
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];
}
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 留意
-
CAEmitterLayer
和CAEmitterCell
中 有相同的特色,他们操控相同的特性 - 若是相同特色都各自设置了值,粒子发射引擎在作业的时分,会把两个值相乘。作为这个特色的终究值来操控显现作用
- 相同特色如下:
@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];
事例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
: 点 -
kCAEmitterLayerLine
: 线
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加载大图
CATiledLayer
是 Core Animation
结构中的一个特别的 CALayer 子类,用于有用地显现大图
或许高分辨率的内容
。
它的作用是将大图切割成小块
,只在需求时才加载和显现
这些小块,以进步功能和内存功率。
原理和作业机制
-
切割大图:
CATiledLayer
会将一个大的图片或许内容切割成多个小的切片(tiles
)。 -
动态加载:
当用户浏览大图时,CATiledLayer
会动态地加载并显现用户所需求的切片,而不是一次性加载整张图片。 -
显现优化:
只要在需求时,CATiledLayer
才会加载和烘托切片,因而它能够在处理大尺度图片或许高分辨率内容时,坚持较低的内存占用和较好的功能体现。 -
多线程处理:
CATiledLayer
运用多线程机制来处理切片的加载和烘托,以进步用户体会和全体功能。
运用办法
-
创立
CATiledLayer
:
经过创立CATiledLayer
目标并将其增加到需求显现大图的视图中。 -
设置署理:
CATiledLayer
的署理目标需求完结drawLayer:inContext:
办法来制造每个切片。 -
指定分辨率和缩放等级:
设置CATiledLayer
的levelsOfDetail
和levelsOfDetailBias
特色来操控切片的分辨率
和显现优先级
。 -
完结署理办法:
完结drawLayer:inContext:
办法,依据给定的rect
和context
制造对应切片的内容。
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)。
- 运用场景:
- 能够依据具体的需求和功能要求来调整切片的尺度。较大的切片尺度或许会进步加载功率,但也会增加内存占用和烘托担负。
- 一般情况下,建议将切片尺度设置为较小的值,以便在加载和显现时能够更好地操控内存运用和功能。
- 作用:
-
levelsOfDetail
代码示例:
中心代码:
#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
具体大图,能够依据自己的大图进行设置
7.9 CAEAGLLayer|CALayer
CAEAGLLayer
是 Core 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
在一个实在的OpenGL运用中,咱们或许会用 NSTimer
或 CADisplayLink
周期性地每秒钟调用 -drawRect
办法60次,同时会将几许图形生成和制造分开以便不会每次都从头生成三角形的极点(这样也能够让咱们制造其他的一些东西而不是一个三角形罢了),不过上面这个比方现已满意演示了绘图准则了
三、CoreAnimation中心动画
Core Animation
供给高效地动画能力,咱们先依照派生联系的办法来了解一下动画相关类:
1. 动画相关类介绍
派生联系如图所示:
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 | 署理(animationDidStart 、animationDidStop ) |
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. **/
转场动画过渡作用
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
- kCATransitionMoveIn
- kCATransitionPush
- kCATransitionReveal
- kCATransitionCube
- kCATransitionSuckEffect
- kCATransitionOglFlip
- kCATransitionRippleEffect
- kCATransitionPageCurl
- kCATransitionPageUnCurl
- kCATransitionCameraIrisHollowOpen
- kCATransitionCameraIrisHollowClose
4. CAPropertyAnimation|派生自CAAnimation
- 承继自
CAAnimation
,不能直接运用 - 要想创立动画目标,应该运用它的两个子类:
CABasicAnimation
和CAKeyframeAnimation
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操控了,并不会先闪现一下。
- 你点P所在的方位点不动,因为呼应点击的是M。所以咱们应该让P和M同步,如上代码
- 咱们初始化一个view,它的状况为1,咱们给它的layer加个动画,from是0,to是2,设置
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 中将该缓存区保存的图片称为 寄宿图
- 图形烘托流水线
- 支撑从极点开端进行制造(在流水线中,极点会被处理生成纹路)
- 也支撑直接运用纹路(图片)进行烘托。
- 相应地,在实践开发中,制造界面也有两种办法:
- 一种是 手动制造;
- 另一种是 运用图片。
-
对此,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:
制造界说寄宿图的基本原理
- 实践开发中,一般经过承继 UIView 并完结
-
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;
- CALayer 有一个可选的 delegate 特色,完结了
- 终究,由 Core Graphics 制造生成的寄宿图会存入 backing store
4. Core Animation 烘托流水线
4.1 Core Animation Pipeline 烘托流水线
Core Animation 烘托流水线的作业原理
- 事实上,app 自身并不担任烘托,烘托则是由一个独立的进程担任,即
Render Server
进程 - App 经过 IPC 将烘托使命及相关数据提交给
Render Server
-
Render Server
处理完数据后,再传递至GPU
- 终究由
GPU
调用iOS
的图画设备进行显现
Core Animation 流水线的具体进程
-
Handle Events: 首要,由 app 处理作业(Handle Events)
- 如:用户的点击操作,在此进程中 app 或许需求更新
视图树
,相应地,图层树
也会被更新;
- 如:用户的点击操作,在此进程中 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 在物理层上完结了对图画的烘托
-
Decode: 打包好的图层被传输到
-
Display: 显现阶段。终究,GPU 经过
Frame Buffer
、视频操控器等相关部件,将图画显现在屏幕上。需求等 render 完毕的下一个 RunLoop 才触发显现;
对上述进程进行串联,它们履行所耗费的时刻远远超越 16.67 ms,因而为了满意对屏幕的 60 FPS 改写率的支撑,需求将这些进程进行分解,经过流水线的办法进行并行履行,如下图所示
4.2 Commit Transaction 发生了什么
一般开发傍边能影响到的便是 Handle Events
和 Commit Transaction
这两个阶段,这也是开发者接触最多的部分。
Handle Events
便是处理接触作业
等交互作业;
在 Core Animation
流水线中,app 调用 Render Server
前的终究一步 Commit Transaction
其实能够细分为 4 个进程:
Layout
Display
Prepare
Commit
4.2.1 Layout(构建视图)
Layout 阶段首要进行视图构建和布局,具体进程包含:
- 调用重载的
layoutSubviews
办法 - 创立视图,并经过
addSubview
办法增加子视图 - 核算视图布局,即一切的
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 的具体操作
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 操作运用
参阅与引荐
- 01-iOS中心动画高级技巧(翻译官方文档)
- 02-官方文档