iOS动画知识


目录

  • ** UIView 动画 **
  • ** Core Animation **
  • ** FaceBook POP动画 **

在iOS中,图形可分为以下几个层次:


<p>OpenGL ES使用硬件加速接口来处理先进的2d 和3d 呈现。OpenGLES通常由游戏开发者或想实现沉浸式图像体验的开发者使用。OpenGLES框架提供对呈现过程的全部控制,以及提供创建平滑动画所需要的帧速。</p>

  • 创建2D和3D图形
  • 创建更复杂的图形,比如数据虚拟化、模拟飞行,或者视频游戏
  • 访问底层图形设备

<p>Core Graphics也称作Quartz,是对定制的2D向量和图像呈现提供支持的本地绘制引擎。该框架提供的引擎虽然没有OpenGLES引擎速度快,但该框架能够很好地适合于呈现定制的2d图形和动态图像。</p>

  • 创建基于路径的绘图
  • 抗锯齿渲染
  • 添加梯度、图片和颜色
  • Use coordinate-space transformations.
  • 创建、显示和分析PDF文档

<p>Core Animation也是Quartz核心框架的一部分,是优化应用动画体验的基础技术。使用Core Animation可以创建嵌套的对象,并且可以对它们操作、旋转、缩放和转换。使用Core animation,你可以创建动态的用户界面而不用使用更底层的图形API,如OpenGL ES。</p>

  • 创建定制动画
  • 添加定时函数和图形
  • 支持帧动画
  • Specify graphical layout constraints.
  • Group multiple-layer changes into anatomic update.

<p>UIKit视图基于 Core Animation提供视图级别的动画支持。</p>

实现动画三要素

  • 元素(who)
  • 行为(how)
  • 执行(do)

两个概念

1.仿射变换(Affine Transformation或 Affine Map)

是一种二维坐标到二维坐标之间的线性变换,它保持了二维图形的“平直性”(即:直线经过变换之后依然是直线)和“平行性”(即:二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。

  • 变换原理
    <pre><code>struct CGAffineTransform {
    CGFloat a;
    CGFloat b;
    CGFloat c;
    CGFloat d;
    CGFloat tx;
    CGFloat ty;
    };</code></pre>
    结构体矩阵图为:(矩阵图)

因为最后一列总是是(0,0,1),所以有用的信息就是前面两列

对一个view进行仿射变化就相当于对view上的每个点做一个乘法
结果就是:(映射变换)

相当于:
(x, y, 1 ) --> (ax + cy + tx, bx + dy + ty, 1)

如果不看c和b的话

a表示x水平方向的缩放,tx表示x水平方向的偏移

d表示y垂直方向的缩放,ty表示y垂直方向的偏移

如果b和c不为零的话,那么视图肯定发生了旋转

<p>仿射变换可以通过一系列的原子变换的复合来实现,包括:平移(Translation)、缩放(Scale)、翻转(Flip)、旋转(Rotation)和错切(Shear)。
</p>

  • 常用函数
    <pre><code>/// 用来连接两个变换效果并返回。返回的t = t1 * t2
    CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2)

/// 矩阵初始值。[ 1 0 0 1 0 0 ]
CGAffineTransformIdentity

/// 自定义矩阵变换,需要掌握矩阵变换的知识才知道怎么用。参照(x, y, 1 ) --> (ax + cy + tx, bx + dy + ty, 1)
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)

/// 旋转视图。传入参数为 角度 * (M_PI / 180)。等同于 CGAffineTransformRotate(self.transform, angle)
CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)

/// 缩放视图。等同于CGAffineTransformScale(self.transform, sx, sy)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)

/// 平移视图。等同于CGAffineTransformTranslate(self.transform, tx, ty)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
</code></pre>

2.贝塞尔曲线

是应用于二维图形应用程序的数学曲线。

曲线定义:起始点、终止点(也称锚点)、控制点。
通过调整控制点,贝塞尔曲线的形状会发生变化。

  • 线性贝塞尔曲线
由 P0 至 P1 的连续点, 描述的一条线段
  • 二次贝塞尔曲线
原理:

 由 P0 至 P1 的连续点 Q0,描述一条线段
 由 P1 至 P2 的连续点 Q1,描述一条线段。 
 由 Q0 至 Q1 的连续点 B(t),描述一条二次贝塞尔曲线。
  • 三次贝塞尔曲线

** 通用公式:**

** 高阶贝塞尔曲线:**

  • 四次贝塞尔曲线
  • 五次贝塞尔曲线

贝塞尔曲线应用在动画中,描述物件的运动路径等等。

UIBezierPath

使用UIBezierPath类可以创建基于矢量的路径,这个类在UIKit中。此类是Core Graphics框架关于CGPathRef的一个封装。

path如果是基于矢量形状的,都用直线和曲线段去创建。 我们使用直线段去创建矩形和多边形,使用曲线段去创建弧(arc),圆或者其他复杂的曲线形状。 每一段都包括一个或者多个点,绘图命令定义如何去诠释这些点。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths。

使用UIBezierPath画图步骤:

  • 1.创建一个UIBezierPath对象
  • 2.调用-moveToPoint:设置初始线段的起点
  • 3.添加线或者曲线去定义一个或者多个子路径
  • 4.改变UIBezierPath对象跟绘图相关的属性。如,我们可以设置画笔的属性、填充样式等

详情见UIBezierPath_And_CAShapeLayer

===================

一、UIView 动画

** 可实现动画的属性 **
<pre><code>坐标尺寸类: bounds、frame、center
视图显示类: backgroundColor、alpha、hidden
形态变化类: transform</code></pre>

1、首尾式

** 基本写法,代码必须放在Begin和Commit之间: **
<pre><code>[UIView beginAnimations:nil context:nil]; // 开始动画
// Code...
[UIView commitAnimations]; // 提交动画</code></pre>

** 简单例子:**
<pre><code>[UIView beginAnimations:nil context:nil]; // 开始动画
[UIView setAnimationDuration:10.0]; // 动画时长

/**

  • 图像向下移动
    */
    CGPoint point = _imageView.center;
    point.y += 150;
    [_imageView setCenter:point];

[UIView commitAnimations]; // 提交动画</code></pre>

**同时运行多个动画效果: **

<pre><code>[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:3.0];
[_imageView setAlpha:0.0];
[UIView commitAnimations];

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:3.0];
CGPoint point = _imageView.center;
point.y += 150;
[_imageView setCenter:point];
[UIView commitAnimations];</code></pre>
以上代码实现的动画效果为(同时执行):

1、图像向下平移150像像
2、设置图像透明度为0。

其它方法及属性:

以下方法及属性不为全部,只例举部分:

<pre><code>// 开始动画

  • (void)beginAnimations:(NSString *)animationID context:(void *)context;

// 提交动画

  • (void)commitAnimations;

// 设置动画曲线,默认是匀速进行:

  • (void)setAnimationCurve:(UIViewAnimationCurve)curve;

// 设置动画时长:

  • (void)setAnimationDuration:(NSTimeInterval)duration;

// 默认为YES。为NO时跳过动画效果,直接跳到执行后的状态。

  • (void)setAnimationsEnabled:(BOOL)enabled;

// 设置动画延迟执行(delay:秒为单位):

  • (void)setAnimationDelay:(NSTimeInterval)delay;

// 动画的重复播放次数

  • (void)setAnimationRepeatCount:(float)repeatCount;

// 如果为YES,逆向(相反)动画效果,结束后返回动画逆向前的状态; 默认为NO:

  • (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;

// 设置动画代理:

  • (void)setAnimationDelegate:(id)delegate;

// 动画将要开始时执行方法××(必须要先设置动画代理):

  • (void)setAnimationWillStartSelector:(SEL)selector;

// 动画已结束时执行方法××(必须要先设置动画代理):

  • (void)setAnimationDidStopSelector:(SEL)selector;

/**

  • 设置动画过渡效果
  • @param transition 动画的过渡效果
  • @param view 过渡效果作用视图
  • @param cache 如果为YES,开始和结束视图分别渲染一次并在动画中创建帧;否则,视图将会渲染每一帧。例如,你不需要在视图转变中不停的更新,你只需要等到转换完成再去更新视图。
    */
  • (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;

转场类型
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
};
</code></pre>

2、block:

  • 方法一:
    <pre><code>[UIView animateWithDuration:4.0 // 动画时长
    animations:^{
    // code
    }];</code></pre>

  • 方法二:
    <pre><code>[UIView animateWithDuration:4.0 // 动画时长
    animations:^{
    // code...
    }
    completion:^(BOOL finished) {
    // 动画完成后执行
    // code...
    }];</code></pre>

  • 方法三:

<pre><code>[UIView animateWithDuration:4.0 // 动画时长
delay:2.0 // 动画延迟
options:UIViewAnimationOptionCurveEaseIn // 动画过渡效果
animations:^{
// code...
}
completion:^(BOOL finished) {
// 动画完成后执行
// code...
}];</code></pre>

  • 方法四,Spring Animationring Animation):弹簧动画

在IOS7开始,系统动画效果广泛应用Spring Animation:

<pre><code>[UIView animateWithDuration:4.0 // 动画时长
delay:0.0 // 动画延迟
usingSpringWithDamping:1.0 // 类似弹簧振动效果 0~1
initialSpringVelocity:5.0 // 初始速度
options:UIViewAnimationOptionCurveEaseInOut // 动画过渡效果
animations:^{
// code...
CGPoint point = _imageView.center;
point.y += 150;
[_imageView setCenter:point];
} completion:^(BOOL finished) {
// 动画完成后执行
// code...
[_imageView setAlpha:1];
}];
</code></pre>
usingSpringWithDamping:它的范围为 0.0f 到 1.0f ,数值越小「弹簧」的振动效果越明显。
initialSpringVelocity:初始的速度,数值越大一开始移动越快。值得注意的是,初始速度取值较高而时间较短时,也会出现反弹情况。

转:Spring Animation 是线性动画或 ease-out 动画的理想替代品。由于 iOS 本身大量使用的就是 Spring Animation,用户已经习惯了这种动画效果,因此使用它能使 App 让人感觉更加自然,用 Apple 的话说就是「instantly familiar」。此外,Spring Animation 不只能对位置使用,它适用于所有可被添加动画效果的属性。
  • 方法六,关键帧动画:

    UIView动画已经具备高级的方法来创建动画,而且可以更好地理解和构建动画。IOS7以后苹果新加了一个animateKeyframesWithDuration的方法,我们可以使用它来创建更多更复杂更酷炫的动画效果,而不需要去使用到核心动画(CoreAnimation)。

创建关键帧方法:

<pre><code>/**

  • 添加关键帧方法
  • @param duration 动画时长
  • @param delay 动画延迟
  • @param options 动画效果选项
  • @param animations 动画执行代码
  • @param completion 动画结束执行代码
    */
  • (void)animateKeyframesWithDuration:(NSTimeInterval)duration
    delay:(NSTimeInterval)delay
    options:(UIViewKeyframeAnimationOptions)options
    animations:(void (^)(void))animations
    completion:(void (^)(BOOL finished))completion;
    </code></pre>

添加关键帧方法:

<pre><code>/**

  • 添加关键帧
  • @param frameStartTime 动画相对开始时间
  • @param frameDuration 动画相对持续时间
  • @param animations 动画执行代码
    */
  • (void)addKeyframeWithRelativeStartTime:(double)frameStartTime
    relativeDuration:(double)frameDuration
    animations:(void (^)(void))animations;
    </code></pre>以上说的相对时间,也就是说:“它们自身会根据动画总持续时长自动匹配其运行时长”。

<pre><code>[UIView animateKeyframesWithDuration:4.0
delay:0.0
options:UIViewKeyframeAnimationOptionCalculationModeCubic | UIViewAnimationOptionCurveLinear
animations:keyFrameBlock
completion:^(BOOL finished) {
// 动画完成后执行
// code...
}];
</code></pre>
动画过渡效果(Options),新增了以下几个:
<pre><code>UIViewKeyframeAnimationOptionCalculationModeLinear = 0 << 10, // default
UIViewKeyframeAnimationOptionCalculationModeDiscrete = 1 << 10,
UIViewKeyframeAnimationOptionCalculationModePaced = 2 << 10,
UIViewKeyframeAnimationOptionCalculationModeCubic = 3 << 10,
UIViewKeyframeAnimationOptionCalculationModeCubicPaced = 4 << 10
</code></pre>
下面我们看一张图,让我们更容易理解:

alt animation
alt animation
  • 方法五,转场动画

<pre><code>/**

  • 转场动画
  • @param fromView 当前View
  • @param toView 要显示的View
  • @param duration 动画持续时间
  • @param option 动画类型
  • @param animations 动画执行代码
  • @param completion 动画结束后,会自动调用这个block
    */
  • (void)transitionFromView:(UIView *)fromView
    toView:(UIView *)toView
    duration:(NSTimeInterval)duration
    options:(UIViewAnimationOptions)options
    completion:(void (^)(BOOL finished))completion;
    </code></pre>

小结:

  • 如果只是修改控件的属性,使用首尾式动画还是比较方便的,但是如果需要在动画完成后做后续处理,就不是那么方便了。
  • 在实际的开发中更常用的时block代码块来处理动画操作,块动画相对来说比较灵活,尤为重要的是能够将动画相关的代码编写在一起,便于代码的阅读和理解。

详情见UIViewAnimations Demo

补充

1.UIImageView的帧动画

UIImageView可以让一系列的图片在特定的时间内按顺序显示

相关属性解析:

animationImages:要显示的图片(一个装着UIImage的NSArray)

animationDuration:完整地显示一次animationImages中的所有图片所需的时间

animationRepeatCount:动画的执行次数(默认为0,代表无限循环)

相关方法解析:

- (void)startAnimating; 开始动画

- (void)stopAnimating;  停止动画

- (BOOL)isAnimating;  是否正在运行动画

</code></prep>

  • 这种方法在某些场景下是可以达到逐帧的动画效果,但是它也存在着很大的性能问题,并且这种方法一旦设置完图片中间的过程就无法控制了。

2.UIActivityIndicatorView

是一个旋转进度轮,可以用来告知用户有一个操作正在进行中,一般用initWithActivityIndicatorStyle初始化

UIActivityIndicatorViewStyle有3个值可供选择:

UIActivityIndicatorViewStyleWhiteLarge   //大型白色指示器    

UIActivityIndicatorViewStyleWhite        //标准尺寸白色指示器    

UIActivityIndicatorViewStyleGray         //灰色指示器,用于白色背景

方法解析:

- (void)startAnimating; 开始动画

- (void)stopAnimating;  停止动画

- (BOOL)isAnimating;  是否正在运行动画

3.UIDynamic

一、简单介绍
1.什么是UIDynamic
  • UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架

  • 可以认为是一种物理引擎,能模拟和仿真现实生活中的物理现象。如:重力、弹性碰撞等现象

2.物理引擎的价值
  • 广泛用于游戏开发,经典成功案例是“愤怒的小鸟”

  • 让开发人员可以在远离物理学公式的情况下,实现炫酷的物理仿真效果

  • 提高了游戏开发效率,产生更多优秀好玩的物理仿真游戏

3.知名的2D物理引擎
  • Box2d

  • Chipmunk

二、使用步骤

要想使用UIDynamic来实现物理仿真效果,大致的步骤如下

  1. 创建一个物理仿真器(顺便设置仿真范围)

  2. 创建相应的物理仿真行为(顺便添加物理仿真元素)

  3. 将物理仿真行为添加到物理仿真器中,开始仿真

三、相关说明
1.三个概念

(1)谁要进行物理仿真?

物理仿真元素(Dynamic Item

(2)执行怎样的物理仿真效果?怎样的动画效果?

物理仿真行为(Dynamic Behavior

(3)让物理仿真元素执行具体的物理仿真行为

物理仿真器(Dynamic Animator

2.物理仿真元素

注意:

  • 不是任何对象都能做物理仿真元素
  • 不是任何对象都能进行物理仿真

物理仿真元素要素:

  • 任何遵守了UIDynamicItem协议的对象

  • UIView默认已经遵守了UIDynamicItem协议,因此任何UI控件都能做物理仿真

  • UICollectionViewLayoutAttributes类默认也遵守UIDynamicItem协议

3.物理仿真行为

(1)UIDynamic提供了以下几种物理仿真行为

  • UIGravityBehavior:重力行为

  • UICollisionBehavior:碰撞行为

  • UISnapBehavior:捕捉行为

  • UIPushBehavior:推动行为

  • UIAttachmentBehavior:附着行为

  • UIDynamicItemBehavior:动力元素行为

(2)物理仿真行为须知

  • 上述所有物理仿真行为都继承自UIDynamicBehavior

  • 所有的UIDynamicBehavior都可以独立进行

  • 组合使用多种行为时,可以实现一些比较复杂的效果

4. 物理仿真器

(1)物理仿真器须知

  • 它可以让物理仿真元素执行物理仿真行为

  • 它是UIDynamicAnimator类型的对象

(2)UIDynamicAnimator的初始化

  • - (instancetype)initWithReferenceView:(UIView *)view;

    view参数:是一个参照视图,表示物理仿真的范围

5.物理仿真器的说明

(1)UIDynamicAnimator的常见方法

- (void)addBehavior:(UIDynamicBehavior *)behavior;//添加1个物理仿真行为

- (void)removeBehavior:(UIDynamicBehavior *)behavior;//移除1个物理仿真行为

- (void)removeAllBehaviors;//移除之前添加过的所有物理仿真行为

(2)UIDynamicAnimator的常见属性

@property (nonatomic, readonly) UIView* referenceView; //参照视图

@property (nonatomic, readonly, copy) NSArray* behaviors;//添加到物理仿真器中的所有物理仿真行为

@property (nonatomic, readonly, getter = isRunning) BOOL running;//是否正在进行物理仿真

@property (nonatomic, assign) id <UIDynamicAnimatorDelegate> delegate;//代理对象(能监听物理仿真器的仿真过程,比如开始和结束)

详情见DynamicAnimationDemo

================

二、Core Animation

什么是Animation(动画),简单点说就是在一段时间内,显示的内容发生了变化.对CALayer来说就是在一段时间内,其Animatable Property发生了变化.
这里涉及到两个东西: 一是Layer(基类CALayer),一是Animation(基于CAAnimation). Animation作用于Layer.CALayer提供了接口用于给自己添加Animation.

Core Animation是一组非常强大的动画处理API,使用它能做出非常绚丽的动画效果,而且往往是事半功倍,使用它需要添加QuartzCore .framework和引入对应的框架<QuartzCore/QuartzCore.h> .

开发步骤:

1> 初始化一个动画对象(CAAnimation)并设置一些动画相关属性.

2> 添加动画对象到层(CALayer)中,开始执行动画.

Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程.

1. CALayer

CALayer与UIView的关系

在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本标签、一个文本输入框、一个图标等等,这些都是UIView。

其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层:

  • 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层。

    @property(nonatomic,readonly,strong) CALayer *layer;

当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示。

换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能。

因此,通过调节CALayer对象,可以很方便的调整UIView的一些外观属性。

  • 对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView可以。所以,如果显示出来的东西需要跟用户进行交互的话,用UIView;如果不需要跟用户进行交互,用UIView或者CALayer都可以。当然,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级。

Layer的渲染架构

Layer也和View一样存在着一个层级树状结构,称之为图层树(Layer Tree),直接创建的或者通过UIView获得的(view.layer)用于显示的图层树,称之为模型树(Model Tree),模型树的背后还存在两份图层树的拷贝,一个是呈现树(Presentation Tree),一个是渲染树(Render Tree)。

呈现树可以通过普通layer(其实就是模型树)的layer.presentationLayer获得,而模型树则可以通过modelLayer属性获得。

  • 模型树的属性在其被修改的时候就变成了新的值,这个是可以用代码直接操控的部分;
  • 呈现树的属性值和动画运行过程中界面上看到的是一致的;
  • 渲染树是私有的,你无法访问到,渲染树是对呈现树的数据进行渲染。

为了不阻塞主线程,渲染的过程是在单独的进程或线程中进行的,所以你会发现Animation的动画并不会阻塞主线程.

隐式动画

根层与非根层:

每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)

所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画

当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果,而这些属性称为Animatable Properties(可动画属性)。

可以通过事务关闭隐式动画:
CATransaction 是核心动画里面负责协调多个动画原子更新显示操作。事务支持嵌套使用。
<pre><code>[CATransaction begin];
// 关闭隐式动画
[CATransaction setDisableActions:YES];

self.myview.layer.position = CGPointMake(10, 10);

[CATransaction commit];</code></pre>

CALayer的基本属性

<pre><code>position和anchorPoint的作用
@property CGPoint position;

用来设置CALayer在父层中的位置
以父层的左上角为原点(0, 0)

@property CGPoint anchorPoint;

称为“定位点”、“锚点”,
决定着CALayer身上的哪个点会在position属性所指的位置。
以自己的左上角为原点(0, 0),
它的x、y取值范围都是0~1,默认值为中心点(0.5, 0.5)

anchorPoint和position的关系举例:

假如锚点anchorPoint为默认值即中点(0.5,0.5),而该层的position设置为(0,0)即为父层的左上点,那么该层在父层中只会看到四分之一的部分。
</code></pre>
为了进一步说明anchorPoint的作用,假设有一个层大小100*100,现在中心点位置(50,50),由此可以得出frame(0,0,100,100)。上面说过anchorPoint默认为(0.5,0.5),同中心点position重合,此时使用图形描述如图1;当修改anchorPoint为(0,0),此时锚点处于图层左上角,但是中心点poition并不会改变,因此图层会向右下角移动,如图2;然后修改anchorPoint为(1,1,),position还是保持位置不变,锚点处于图层右下角,此时图层如图3。


2.CAAnimation概述

当需要对非Root Layer进行动画或者需要对动画做更多自定义的行为的时候,就必须使用到显式动画了,显式动画的基类为CAAnimation,显式动画不会改变该属性的值,它只是用于动画显示。

  • 采用了CAMediaTiming协议:可以调整时间,包括持续时间,速度,重复次数,并且能设定图层过渡;
  • 采用了CAAction协议:可以通过响应动作的方式来显示动画.

来看下CAAnimation的继承体系

  • CAAnimation是个抽象类,不具备动画效果,必须用它的子类才有动画效果。

  • CAPropertyAnimation也是个抽象类,本身不具备动画效果,只有子类才有。

    CABasicAnimation和CAKeyframeAnimation:

CABasicAnimation基本动画,做一些简单效果。
CAKeyframeAnimation关键帧动画,做一些连续的流畅的动画。
  • CAAnimationGroup是个动画组,可以同时进行缩放,旋转(同时进行多个动画)。
  • CATransition是转场动画,界面之间跳转(切换)都可以用转场动画。
1)CAAnimation——简介

是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类。

基本属性说明:
  • duration:动画的持续时间

  • repeatCount:重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT

  • repeatDuration: 重复时间

  • removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards

  • fillMode: 决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之

    fillMode属性的设置:

       kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
    
       kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
    
       kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。
    
       kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
    
  • beginTime: 可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间

  • timingFunction: 速度控制函数,控制动画运行的节奏

    速度控制函数(CAMediaTimingFunction):

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

CAAnimation在分类中定义了代理方法

<pre><code>
@interface NSObject (CAAnimationDelegate)

/* Called when the animation begins its active duration. */
// 动画开始时调用

  • (void)animationDidStart:(CAAnimation *)anim;

/* Called when the animation either completes its active duration or

  • is removed from the object it is attached to (i.e. the layer). 'flag'
  • is true if the animation reached the end of its active duration
  • without being removed. */
    // 动画结束后调用
  • (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

@end</code></pre>

2)CAPropertyAnimation

是CAAnimation的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:CABasicAnimationCAKeyframeAnimation

基本属性说明:
keyPath:    通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@“position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果
CABasicAnimation——基本动画

属性说明:

fromValue:  keyPath相应属性的初始值
toValue:    keyPath相应属性的结束值
byValue:    keyPath相应属性的改变值

动画过程说明:

随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue。

keyPath内容是CALayer的可动画Animatable属性。

如果fillMode = kCAFillModeForwards同时removedOnComletion = NO,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。

CAKeyframeAnimation——关键帧动画

关键帧动画,也是CAPropertyAnimation的子类,与CABasicAnimation的区别是:

CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值

CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation

属性说明:

values: NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧

path: 可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略

keyTimes: 可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的

3)CAAnimationGroup——动画组

动画组,是CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。

默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间。

属性说明:

animations: 用来保存一组动画对象的NSArray

4)CATransition——转场动画

CATransition是CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。

UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果。

属性说明:

type: 动画过渡类型

subtype: 动画过度方向

subtype

startProgress: 动画起点(在整体动画的百分比)

endProgress: 动画终点(在整体动画的百分比)

步骤

1.创建转场动画

2.设置转场类型、子类型(可选)及其他属性

3.设置转场后的新视图并添加动画到图层

举例
<pre><code>CATransition *anim = [CATransition animation];
anim.type = @"cube";
anim.subtype = kCATransitionFromBottom;
[view.layer addAnimation:anim forKey:nil];
</code></pre>

详情见KKCoreAnimation&&SKBounceAnimation

3.补充

1)CASpringAnimation

iOS9才引入的动画类,它继承于CABaseAnimation,用于制作弹簧动画

参数说明

mass://质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大,动画的速度变慢,并且波动幅度变大

stiffness://刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快

damping://阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快

initialVelocity://初始速率,动画视图的初始速度大小
速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反

settlingDuration://结算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
通常弹簧动画的时间使用结算时间比较准确

2)CAShapeLayer:

CAShapeLayer顾名思义,继承于CALayer。 每个CAShapeLayer对象都代表着将要被渲染到屏幕上的一个任意的形状(shape)。具体的形状由其path(类型为CGPathRef)属性指定。 普通的CALayer是矩形,所以需要frame属性。CAShapeLayer初始化时也需要指定frame值,但 它本身没有形状,它的形状来源于其属性path 。CAShapeLayer有不同于CALayer的属性,它从CALayer继承而来的属性在绘制时是不起作用的。

3)CADisplayLink

CADisplayLink是一个计时器,但是同NSTimer不同的是,CADisplayLink的刷新周期同屏幕完全一致。例如在iOS中屏幕刷新周期是60次/秒,CADisplayLink刷新周期同屏幕刷新一致也是60次/秒,这样一来使用它完成的逐帧动画(又称为“时钟动画”)完全感觉不到动画的停滞情况。

iOS程序在运行后就进入一个消息循环中(这个消息循环称为“主运行循环”),整个程序相当于进入一个死循环中,始终等待用户输入。将CADisplayLink加入到主运行循环队列后,它的时钟周期就和主运行循环保持一致,而主运行循环周期就是屏幕刷新周期。在CADisplayLink加入到主运行循环队列后就会循环调用目标方法,在这个方法中更新视图内容就可以完成逐帧动画。

使用方法:
<pre><code>定义CADisplayLink并制定触发调用方法
将显示链接添加到主运行循环队列
// 定义
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(rotationChange)];
// 添加到主循环队列
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
开始和暂停
// 暂停
link.paused = YES;
// 开始
link.paused = NO;
</code></pre>

三、FaceBook POP动画

1.工作机制

POP是一个在iOS与OS X上通用的极具扩展性的动画引擎 它在基本的静态动画的基础上增加的弹簧动画与衰减动画。

使之能创造出更真实更具物理性的交互动画 POP的API可以快速的与现有的ObjC代码集成并可以作用于任意对象的任意属性。

POP 本质上是基于定时器的动画库,使用每秒 60 频率的定时器,即时钟频率为 1/60 秒(为了匹配 iOS 显示屏帧率),使得动画刷新绘制频率与屏幕刷新频率一致。很多这类动画库都使用 CADisplayLink 做为一个回调源。

在计算机的世界里面,其实并不存在绝对连续的动画,你所看到的屏幕上的动画本质上都是离散的,只是在一秒的时间里面离散的帧多到一定的数量人眼就觉得是连续的了,在iOS中,最大的帧率是60帧每秒。 iOS提供了Core Animation框架,只需要开发者提供关键帧信息,比如提供某个animatable属性终点的关键帧信息,然后中间的值则通过一定的算法进行插值计算,从而实现补间动画。 Core Aniamtion中进行插值计算所依赖的时间曲线由CAMediaTimingFunction提供。 Pop Animation在使用上和Core Animation很相似,都涉及Animation对象以及Animation的载体的概念,不同的是Core Animation的载体只能是CALayer,而Pop Animation可以是任意基于NSObject的对象。当然大多数情况Animation都是界面上显示的可视的效果,所以动画执行的载体一般都直接或者间接是UIView或者CALayer。

  • Pop Animation相比于Core Animation的优点

      1. Pop Animation应用于CALayer时,在动画运行的任何时刻,layer和其presentationLayer的相关属性值始终保持一致,而Core Animation做不到。 
      2.Pop Animation可以应用任何NSObject的对象,而Core Aniamtion必须是CALayer。
    

2.源码结构:

代码包括四个目录:

Animations: 定义pop支持的动画类型,并抽像各种动画的低层数据结构

Engine:组织动化运行的数据结构,核心是动化管理者,还包括了动画引擎所需要的可动画属性定义、动画追踪等内容

Utility: 封装Engine中用到的各种功能,包括数值运算,数据转换,运算等

WebCore: 这部分是苹果公司的代码,矩阵运算和贝赛尔曲线的,用于底层运算。

1)Animations

 POPAnimation :定义了动画基类POPAnimation
    私有变量:结构体`_POPAnimationState` 纪录动画状态,是实现动画最关键的属性
    公有变量:name beginTime delegate tracer(动画追踪,纪录各种动画事件) ^completionBlock removedOnCompletion paused
继承关系:
    
NSObject
 POPAnimation
     POPPropertyAnimation
          POPBasicAnimation
          POPDecayAnimation
          POPSpringAnimation
     POPCustomAnimation

 _POPAnimationState
    _POPPropertyAnimationState
        _POPBasicAnimationState
        _POPDecayAnimationState
        _POPSpringAnimationState

NSObject (POP)让所有对象增加pop动画支持(增删改查)通过POPAnimator进行管理

2)Engine

POPAnimateableProperty 可动画的属性,用于定义动画类型和动画时修改值
    POPStaticAnimatablePropertyState 结构体纪录了属性名,readBlock,writeBlock,和域值

    POPStaticAnimatablePropertyState _staticStates[]  纪录了所有可动画的属性对应的名称,其中的writeblock功能是把二进制数据写到对应的属性里

类族:

POPAnimatableProperty
    POPConcreteAnimatableProperty
    POPMutableAnimatableProperty
    POPPlaceholderAnimatableProperty
    POPStaticAnimatableProperty
    
POPAnimationEvent POPAnimationTracer的基本元素

POPAnimationExtras 扩展CAAnimation 和POPSpringAnimation 的方法(分类)

POPAnimationRuntime 这里有几个函数比较特殊
    POPBox 包装一个函数vector,把vector转化为point,size,rect,color等
    POPUnbox 解包一个向量,把point,size,rect,color对象转换为vector

POPAnimationTracer 动画跟踪 通过POPAnimationEvent来描述

POPAnimator 动画管理者,纪录所有的动画事件(POPAnimatorItem,纪录了动画作用到的对象、动画key、动画对象等信息),初始化是把CADisplayLink注册到runloop里定时触发render过程,进行一帧动画渲染。她提供了add、remove等操作Animation集合的方法,并提供了Observer和delegate等事件函数
POPAnimator通过两层(key,view)存储动画,(动画目标对象,(动画key,动画对象))
具体每帧的渲染过程有两步:1.renderTime,更新state的状态。 2.updateAnimatable,把state的变化更新到obj的具体属性上

3)Utility 各种工具类

POPAction: ActionDisabler和ActionEnabler  利用RAII暂停核心动画以ActionEnabler为例,创建ActionEnabler时停止动画,销毁时重新运行动画

POPCGUtils:颜色转换函数,数组和点,point,size,rect,color等转换函数

POPGeometry:扩展NSValue到POP自定义类型的转换

POPLayerExtras:图层矩阵变换封装,底层由TransformationMatrix实现

POPMath:数学计算接口,封装了UnitBezier,POPVector和其他数字计算

SpringSolver:spring插值计算

POPVector:向量运算

4)WebCore

FloatConversion:浮点数转换方法,提供double->float和double->CGFloat,
用途:服务于矩阵运算

TransformationMatrix:矩阵运算,包括初始化,scale,rotate,translate,flipX,flipY,skew,applyPerspective???,multVecMatrix向量和矩阵相乘等矩阵操作,
CATransform3D 矩阵变换之立方体旋转实现细节 
关键方法说明:
计算行列式的值:不列举了,很枯燥,行列式计算的基本概念,从二阶计算到三阶再计算到四阶。主要作用是引擎层对layer进行坐标变换

UnitBezier: 结构体,初始化参数为两个控制点p1(p1x,p1y),p2(p2x,p2y),用于表示起始点为s(0,0),终止点为e(1,1),p1为第一控制点,p2为第二控制点的二次Bezier。
用途:重新实现系统的自带动画
方法说明:
UnitBezier(double p1x, double p1y, double p2x, double p2y)//用两个控制点的坐标初始化曲线,
double sampleCurveX(double t)//通过参数t计算对应的x值
double sampleCurveY(double t)//通过参数t计算对应的y值
double sampleCurveDerivativeX(double t)
//epsilon表示一个小间距,相当于数学计算里的dX,一般为全距离的1/1000,
//求解x对应的t值,用到了函数sampleCurveDerivativeX
double solveCurveX(double x, double epsilon)
double solve(double x, double epsilon)//求解x对应的y值,分为两步算,1.由x计算t;2.由t计算y

3.动画执行流程:

1)POPAnimator是单例对象,初始化对象实例时注册把CADisplayLink到runloop,定时调用render,通过renderTime函数遍历所有正在执行的动画,使动画进行到下一帧,并刷新画面,直到所有动画推进完成。

2)动画状态的推进最终都是通过_POPAnimationState类和其子类的advance方法推进的,advance更新时间戳,updateAnimatable方法把state的状态变更到动画要作用的obj对象。

3)Spring、Decay、Basic 都有各自实现的advance函数计算,而Custom方式不同,他是通过一个POPCustomAnimationBlock类型的回调来就算这一帧的值的。

4.动画使用

1)POPBasicAnimation

基本动画,接口方面和CABasicAniamtion很相似,使用可以提供初始值fromValue,这个 终点值toValue,动画时长duration以及决定动画节奏的timingFunction。timingFunction直接使用的CAMediaTimingFunction,是使用一个横向纵向都为一个单位的拥有两个控制点的贝赛尔曲线来描述的,横坐标为时间,纵坐标为动画进度。

NSInteger height = CGRectGetHeight(self.view.bounds); 
NSInteger width = CGRectGetWidth(self.view.bounds); 

CGFloat centerX = arc4random() % width; 
CGFloat centerY = arc4random() % height; 

POPBasicAnimation *anim = [POPBasicAnimation animationWithPropertyNamed:kPOPViewCenter]; 
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(centerX, centerY)]; 
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; 
anim.duration = 0.4; 
[self.testView pop_addAnimation:anim forKey:@"centerAnimation"]; 
2)PopSpringAnimation

弹簧动画是Bezier曲线无法表述的,所以无法使用PopBasicAniamtion来实现。PopSpringAnimation便是专门用来实现弹簧动画的。

POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPViewCenter]; 

NSInteger height = CGRectGetHeight(self.view.bounds); 

NSInteger width = CGRectGetWidth(self.view.bounds); 

CGFloat centerX = arc4random() % width; 

CGFloat centerY = arc4random() % height; 

anim.toValue = [NSValue valueWithCGPoint:CGPointMake(centerX, centerY)]; 

anim.springBounciness = 16; 

anim.springSpeed = 6; 

[self.testView pop_addAnimation:anim forKey:@"center"]; 

属性介绍

  • springBounciness 弹簧弹力 取值范围为[0, 20],默认值为4

  • springSpeed 弹簧速度,速度越快,动画时间越短 [0, 20],默认为12,和springBounciness一起决定着弹簧动画的效果

  • dynamicsTension 弹簧的张力

  • dynamicsFriction 弹簧摩擦

  • dynamicsMass 质量 。张力,摩擦,质量这三者可以从更细的粒度上替代springBounciness和springSpeed控制弹簧动画的效果

3)PopDecayAnimation

基于Bezier曲线的timingFuntion同样无法表述Decay Aniamtion,所以Pop就单独实现了一个 PopDecayAnimation,用于衰减动画。衰减动画一个很常见的地方就是 UIScrollView 滑动松开后的减速,这里就基于UIView实现一个自己的ScrollView,然后使用PopDecayAnimation实现 此代码可以详细参见 KKScrollView 的实现,当滑动手势结束时,根据结束的加速度,给衰减动画一个初始的velocity,用来决定衰减的时长。

4)POPCustomAnimation

通过自定义block刷新动画帧,更灵活的方式

POPCustomAnimation 并不是基于POPPropertyAnimation的,它直接继承自PopAnimation用于创建自定义动画用的,通过POPCustomAnimationBlock类型的block进行初始化,

typedef BOOL (^POPCustomAnimationBlock)(id target, POPCustomAnimation *animation); 

此block会在界面的每一帧更新的时候被调用,创建者需要在block中根据当前currentTime和elapsedTime来决定如何更新target的相关属性,以实现特定的动画。当你需要结束动画的时候就在block中返回NO,否则返回YES。

详情见facebook-pop-sample&&POP-Handapp

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容