QuartzCore框架: Core Animation动画相关
一些其他动画
Core Graphics: Quartz2D
demo-知识点-自定义视图
动画相关
核心动画(Core Animation)
开发步骤
- 使用它需要先添加QuartzCore.framework框架和引入主头文件<QuartzCore/QuartzCore.h>
- 初始化一个CAAnimation对象,并设置一些动画相关属性
- 通过调用CALayer的addAnimation:forKey:方法增加CAAnimation对象到CALayer中,这样就能开始执行动画了
- 通过调用CALayer的removeAnimationForKey:方法可以停止CALayer中的动画
注意
- Core Animation是直接作用在CALayer上的,并非UIView。CALayer中很多属性都可以通过CAAnimation实现动画效果,包括:opacity、position、transform、bounds、contents等(可以在API文档中搜索:CALayer Animatable Properties)
- Core Animation的动画执行过程都是在后台操作的,不会阻塞主线程
CAAnimation继承结构
!
CAAnimation
所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类
属性解析:(红色代表来自CAMediaTiming协议的属性)
- duration:动画的持续时间
- repeatCount:动画的重复次数
- repeatDuration:动画的重复时间
- removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
- fillMode:决定当前对象在非active时间段的行为。比如动画开始之前,动画结束之后。
fillMode属性值(要想fillMode有效,最好设置removedOnCompletion = NO)- kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
- kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
- kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。
- kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
- beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2 CACurrentMediaTime()为图层的当前时间
- timingFunction:速度控制函数,控制动画运行的节奏(CAMediaTimingFunction)
- kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感
- kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开
- kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地
- kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。这个是默认的动画行为。
- delegate:动画代理
- (void)animationDidStart:(CAAnimation *)anim;
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
CAPropertyAnimation
是CAAnimation
的子类,也是个抽象类,要想创建动画对象,应该使用它的两个子类:CABasicAnimation
和CAKeyframeAnimation
属性解析:
- keyPath:通过指定CALayer的一个属性名称为keyPath(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@”position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果
CABasicAnimation 基本动画
CAPropertyAnimation
的子类
属性解析:
- fromValue:keyPath相应属性的初始值
- toValue:keyPath相应属性的结束值
- 随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue
- 如果
fillMode=kCAFillModeForwards
和removedOnComletion=NO
,那么在动画执行完毕后,图层会保持显示动画执行后的状态。但在实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变。比如,CALayer的position初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为(100,100),虽然动画执行完毕后图层保持在(100,100)这个位置,实质上图层的position还是为(0,0)。
CAKeyframeAnimation 关键帧动画
CApropertyAnimation的子类,跟CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值。 属性解析:
- values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
- path:可以设置一个
CGPathRef\CGMutablePathRef
,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略 - keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。当keyTimes没有设置的时候,各个关键帧的时间是平分的
CABasicAnimation可看做是最多只有2个关键帧的CAKeyframeAnimation
CAAnimationGroup
CAAnimation的子类,可以保存一组动画对象,将CAAnimationGroup对象加入层后,组中所有动画对象可以同时并发运行。
属性解析:
- animations:用来保存一组动画对象的NSArray
- 默认情况下,一组动画对象是同时运行的,也可以通过设置动画对象的beginTime属性来更改动画的开始时间
CATransition
CAAnimation的子类,用于做转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。iOS比Mac OS X的转场动画效果少一点。UINavigationController就是通过CATransition实现了将控制器的视图推入屏幕的动画效果 属性解析:
- type:动画过渡类型
- subtype:动画过渡方向
- startProgress:动画起点(在整体动画的百分比)
- endProgress:动画终点(在整体动画的百分比)
转场动画过渡效果
UIView动画
UIKit直接将动画集成到UIView类中,当内部的一些属性发生改变时,UIView将为这些改变提供动画支持。执行动画所需要的工作由UIView类自动完成,但仍要在希望执行动画时通知视图,为此需要将改变属性的代码放在[UIView beginAnimations:nil context:nil]
和[UIView commitAnimations]
之间。
常见方法解析:
+ (void)setAnimationDelegate:(id)delegate
: 设置动画代理对象,当动画开始或者结束时会发消息给代理对象
+ (void)setAnimationWillStartSelector:(SEL)selector
: 当动画即将开始时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
+ (void)setAnimationDidStopSelector:(SEL)selector
: 当动画结束时,执行delegate对象的selector,并且把beginAnimations:context:中传入的参数传进selector
+ (void)setAnimationDuration:(NSTimeInterval)duration
: 动画的持续时间,秒为单位
+ (void)setAnimationDelay:(NSTimeInterval)delay
: 动画延迟delay秒后再开始
+ (void)setAnimationStartDate:(NSDate *)startDate
: 动画的开始时间,默认为now
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve
: 动画的节奏控制
+ (void)setAnimationRepeatCount:(float)repeatCount
: 动画的重复次数
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses
: 如果设置为YES,代表动画每次重复执行的效果会跟上一次相反
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache
: 设置视图view的过渡效果,transition指定过渡类型,cache设置YES代表使用视图缓存,性能较好
Block动画
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
属性解析:
duration
:动画的持续时间
delay
:动画延迟delay秒后开始
options
:动画的节奏控制
animations
:将改变视图属性的代码放在这个block中
completion
:动画结束后,会自动调用这个block
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion;
方法调用完毕后,相当于执行了下面两句代码: // 添加toView到父视图[fromView.superview addSubview:toView]; // 把fromView从父视图中移除[fromView.superview removeFromSuperview];
例如一个视图淡出屏幕,另外一个视图出现的代码:
[UIView animateWithDuration:1.0 animations:^{
firstView.alpha = 0.0;
secondView.alpha = 1.0;
}];
如何实现连续的动画?可以在completion代码块中添加动画。下面是实例代码:
[UIView animateWithDuration:2.0 animations:^{
oldImageView.alpha =0.0;
newImageView.alpha = 1.0;
//imageView.center = CGPointMake(500.0, 512.0);
} completion:^(BOOL finished) {
[UIView animateWithDuration:4.0 animations:^{
newImageView.center = CGPointMake(500.0, 512.0);
}];
}];
CALayer 动画
#pragma mark 暂停CALayer的动画
-(void)pauseLayer:(CALayer*)layer {
CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
// 让CALayer的时间停止走动
layer.speed = 0.0;
// 让CALayer的时间停留在pausedTime这个时刻
layer.timeOffset = pausedTime;
}
#pragma mark 恢复CALayer的动画
-(void)resumeLayer:(CALayer*)layer {
CFTimeInterval pausedTime = layer.timeOffset;
// 1. 让CALayer的时间继续行走
layer.speed = 1.0;
// 2. 取消上次记录的停留时刻
layer.timeOffset = 0.0;
// 3. 取消上次设置的时间
layer.beginTime = 0.0;
// 4. 计算暂停的时间(这里也可以用CACurrentMediaTime()-pausedTime)
CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
// 5. 设置相对于父坐标系的开始时间(往后退timeSincePause)
layer.beginTime = timeSincePause;
}
CADisplayLink
CADisplayLink是一种以屏幕刷新频率触发的时钟机制,每秒钟执行大约60次左右。 CADisplayLink是一个计时器,可以使绘图代码与视图的刷新频率保持同步,而NSTimer无法确保计时器实际被触发的准确时间。 使用方法:
- 定义CADisplayLink并制定触发调用方法
- 将显示链接添加到主运行循环队列
UIImageView的帧动画
UIImageView可以让一系列的图片在特定的时间内按顺序显示
相关属性解析:
animationImages
:要显示的图片(一个装着UIImage的NSArray)
animationDuration
:完整地显示一次animationImages中的所有图片所需的时间
animationRepeatCount
:动画的执行次数(默认为0,代表无限循环)
相关方法解析:
- (void)startAnimating;
: 开始动画
- (void)stopAnimating;
: 停止动画
- (BOOL)isAnimating;
: 是否正在运行动画
UIActivityIndicatorView
是一个旋转进度轮,可以用来告知用户有一个操作正在进行中,一般用initWithActivityIndicatorStyle初始化 方法解析:
- (void)startAnimating;
开始动画
- (void)stopAnimating;
停止动画
- (BOOL)isAnimating;
; 是否正在运行动画
UIActivityIndicatorViewStyle有3个值可供选择:
UIActivityIndicatorViewStyleWhiteLarge
//大型白色指示器
UIActivityIndicatorViewStyleWhite 
//标准尺寸白色指示器
UIActivityIndicatorViewStyleGray
//灰色指示器,用于白色背景
Quartz2D
Quartz2D的API是纯C语言的。来自于Core Graphics。是一个二维绘图引擎,同时支持iOS和Mac系统。 数据类型和函数基本都以CG作为前缀,CGContextRef、CGPathRef、CGContextStrokePath(ctx)。 Quartz2D在iOS开发中很重要的一个价值是:自定义view(自定义UI控件)。
Quartz 2D能完成的工作
- 绘制图形 : 线条\三角形\矩形\圆\弧等
- 绘制文字
- 绘制\生成图片(图像)
- 读取\生成PDF
- 截图\裁剪图片
- 自定义UI控件
- 涂鸦\画板
- 手势解锁
Quartz2D提供了以下几种类型的Graphics Context:
- Bitmap Graphics Context
- PDF Graphics Context
- Window Graphics Context
- Layer Graphics Context
- Printer Graphics Context
相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上:
图形上下文(Graphics Context)
是一个CGContextRef类型的数据。
图形上下文的作用
- 保存绘图信息、绘图状态
- 决定绘制的输出目标(绘制到什么地方去?) 输出目标可以是PDF文件、Bitmap或者显示器的窗口上)
如何利用Quartz2D自定义view?(自定义UI控件)
首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去。 其次,那个图形上下文必须跟view相关联,才能将内容绘制到view上面。
自定义view的步骤
- 新建一个类,继承自UIView
- 实现
- (void)drawRect:(CGRect)rect
方法,然后在这个方法中取得跟当前view相关联的图形上下文 - 绘制相应的图形内容
- 利用图形上下文将绘制的所有内容渲染显示到view上面
为什么要实现drawRect:方法才能绘图到view上?
因为在drawRect:方法中才能取得跟view相关联的图形上下文; View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了; View之所以能显示东西,完全是因为它内部的layer;
drawRect:方法在什么时候被调用?
当view第一次显示到屏幕上时(被加到UIWindow上显示出来)
调用view的setNeedsDisplay
或者setNeedsDisplayInRect:
时
Quartz2D代码
Quartz2D绘图的代码步骤
-
获得图形上下文 CGContextRef ctx = UIGraphicsGetCurrentContext();
-
拼接路径(下面代码是搞一条线段) CGContextMoveToPoint(ctx, 10, 10); CGContextAddLineToPoint(ctx, 100, 100);
-
绘制路径 CGContextStrokePath(ctx); // CGContextFillPath(ctx);
常用拼接路径函数
- 新建一个起点
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y) - 添加新的线段到某个点
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y) - 添加一个矩形
void CGContextAddRect(CGContextRef c, CGRect rect) - 添加一个椭圆
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect) - 添加一个圆弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise)
常用绘制路径函数
- Mode参数决定绘制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode) - 绘制空心路径
void CGContextStrokePath(CGContextRef c) - 绘制实心路径
void CGContextFillPath(CGContextRef c)
提示:一般以CGContextDraw、CGContextStroke、CGContextFill开头的函数,都是用来绘制路径的
图形上下文栈的操作
- 将当前的上下文copy一份,保存到栈顶(那个栈叫做”图形上下文栈”)
void CGContextSaveGState(CGContextRef c) - 将栈顶的上下文出栈,替换掉当前的上下文
void CGContextRestoreGState(CGContextRef c)
画线drawLine
- 获取上下文件UIGraphicsGetCurrentContext();
- 设置起点CGContextMoveToPoint(ctx, 10, 10);
- 添加连接点AddLineToPoint(ctx, 110, 10);
- 渲染CGContextStrokePath(ctx);
- 设置线宽CGContextSetLineWidth(ctx, 3);
- 设置线的颜色CGContextSetRGBStrokeColor(ctx, 1, 0, 0, 1);
- 再添加1个连接点完成矩形绘制
- 设置线的首尾样式CGContextSetLineCap(ctx, kCGLineCapButt);
- 设置线的连接样式CGContextSetLineJoin(ctx, kCGLineJoinRound);
画矩形drawRectangle
- 获取上下文件UIGraphicsGetCurrentContext();
- 设置起点,并连接四个点成为矩形 CGContextMoveToPoint(ctx, 10, 10) AddLineToPoint(ctx, 110, 10); AddLineToPoint(ctx, 110, 110); AddLineToPoint(ctx, 110, 10); AddLineToPoint(ctx, 10, 10);
- 画空心CGContextStrokePath(ctx)
- 画实心CGContextFillPath(ctx);
- 使用CGContextStrokeRect();/ CGContextFillRect(); 画矩形
画三角形triangle
- 获取上下文件UIGraphicsGetCurrentContext();
- 设置起点,并连接三个点
- 关闭路径CGContextClosePath(ctx);
- 渲染CGContextStrokePath(ctx)
画圆circle
CGContextAddEllipseInRect(context, CGRectMake(10, 10, 100, 100));
画弧arc
//x y 圆点 //radius 半径 //startAngle 开始角度 //endAngle 结束角度 //clockwise 圆弧的伸展方向 0 顺时针 1 逆时针 void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int clockwise) CGContextAddArc(context, 100, 100, 50, 0, M_PI_2, 1);
画扇形sector
//设置扇形路径的起点 CGContextMoveToPoint(context, 100, 100); CGContextAddArc(context, 100, 100, 50, M_PI_4, 3 * M_PI_4, 0); CGContextClosePath(context);
UIBezierPath
使用UIBezierPath类可以创建基于矢量的路径,这个类在UIKit中。此类是Core Graphics框架关于path的一个封装。使用此类可以定义简单的形状,如椭圆或者矩形,或者有多个直线和曲线段组成的形状。
Bezier Path 基础 UIBezierPath对象是CGPathRef数据类型的封装。path如果是基于矢量形状的,都用直线和曲线段去创建。我们使用直线段去创建矩形和多边形,使用曲线段去创建弧(arc),圆或者其他复杂的曲线形状。每一段都包括一个或者多个点,绘图命令定义如何去诠释这些点。每一个直线段或者曲线段的结束的地方是下一个的开始的地方。每一个连接的直线或者曲线段的集合成为subpath。一个UIBezierPath对象定义一个完整的路径包括一个或者多个subpaths。
CALayer
其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层。
在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层 @property(nonatomic,readonly,retain) CALayer *layer;
。
当UIView需要显示到屏幕上时,会调用drawRect:
方法进行绘图,并且会将所有内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝到屏幕上,于是就完成了UIView的显示。
UIView本身更像是一个CALayer的管理器。一个UIView上可以有n个CALayer,每个layer显示一种东西,增强UIView的展现能力。
对比CALayer,UIView多了一个事件处理的功能。所以,如果不需要跟用户进行交互,用UIView或者CALayer都可以。当然,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级。
首先CALayer是定义在QuartzCore框架中的;
CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的;
UIColor、UIImage是定义在UIKit框架中的;
QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和Mac OS X上都能使用。
但是UIKit只能在iOS中使用为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef。
CALayer的基本使用
- 通过操作CALayer对象,可以很方便地调整UIView的一些外观属性,比如:阴影、圆角大小、边框宽度和颜色。
- 还可以给图层添加动画,来实现一些比较炫酷的效果。
CALayer的属性
- @property CGRect bounds; 宽度和高度;
- @property CGPoint position; 位置(默认指中点,具体由anchorPoint决定)。用来设置CALayer在父层中的位置。以父层的左上角为原点(0, 0);
- @property CGPoint anchorPoint;
锚点(x,y的范围都是0-1),决定了position的含义。称为“定位点”、“锚点”决定着CALayer身上的哪个点会在position属性所指的位置以自己的左上角为原点(0, 0)它的x、y取值范围都是0~1,默认值为(0.5, 0.5)。 - @property CGColorRef backgroundColor; 背景颜色(CGColorRef类型)
- @property CATransform3D transform; 形变属性
- @property CGColorRef borderColor; 边框颜色(CGColorRef类型)
- @property CGFloat borderWidth; 边框宽度
- @property GCFloat cornerRadius; 圆角半径
- @property(retain) id contents; 内容(比如设置为图片CGImageRef)
UIView与CLayer有什么区别?
- UIView 是 iOS 系统中界面元素的基础,所有的界面元素都是继承自它。它本身完全是由 CoreAnimation 来实现的。它真正的绘图部分,是由一个 CALayer 类来管理。 UIView 本身更像是一个 CALayer 的管理器,访问它的跟绘图和跟坐标有关的属性。
- UIView 有个重要属性 layer ,可以返回它的主 CALayer 实例。
- UIView 的 CALayer 类似 UIView 的子 View 树形结构,也可以向它的 layer 上添加子layer ,来完成某些特殊的表示。即 CALayer 层是可以嵌套的。
- UIView 的 layer 树形在系统内部,被维护着三份 copy 。分别是逻辑树,这里是代码可以操纵的;动画树,是一个中间层,系统就在这一层上更改属性,进行各种渲染操作;显示树,其内容就是当前正被显示在屏幕上得内容。
- 动画的运作:对 UIView 的 subLayer (非主 Layer )属性进行更改,系统将自动进行动画生成,动画持续时间的缺省值似乎是 0.5 秒。
- 坐标系统: CALayer 的坐标系统比 UIView 多了一个 anchorPoint 属性,使用CGPoint 结构表示,值域是 0~1 ,是个比例值。这个点是各种图形变换的坐标原点,同时会更改 layer 的 position 的位置,它的缺省值是 {0.5,0.5} ,即在 layer 的中央。
- 渲染:当更新层,改变不能立即显示在屏幕上。当所有的层都准备好时,可以调用setNeedsDisplay 方法来重绘显示。
- 变换:要在一个层中添加一个 3D 或仿射变换,可以分别设置层的 transform 或affineTransform 属性。
- 变形: Quartz Core 的渲染能力,使二维图像可以被自由操纵,就好像是三维的。图像可以在一个三维坐标系中以任意角度被旋转,缩放和倾斜。 CATransform3D 的一套方法提供了一些魔术般的变换效果。
CALayer的隐式动画
隐式动画每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)。所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画;
-
什么是隐式动画? 当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果。这些属性称为Animatable Properties(可动画属性)。
-
列举几个常见的Animatable Properties: bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画。
backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画。
position:用于设置CALayer的位置。修改这个属性会产生平移动画。 -
可以通过动画事务(CATransaction)关闭默认的隐式动画效果
[CATransaction begin]; [CATransaction setDisableActions:YES]; self.myview.layer.position = CGPointMake(10, 10); [CATransaction commit];