CALayer
一、简介
1.CALayer
简单的说是层(图层)的概念,类似与PS中的图层。一个用来完成绘制、渲染、动画等效果的可见的容器。
2.CALayer 与 UIView 的联系与区别
UIView中有一个layer属性,为根图层。UIView之所以能显示在屏幕上,完全是因为它内部的CALayer对象。在创建UIView对象时,UIView内部会自动创建一个层(即CALayer对象),通过UIView的layer属性可以访问这个层。当UIView需要显示到屏幕上时,会调用drawRect:方法进行绘图,并且会将所有内容绘制在自己的层上,绘图完毕后,系统会将层拷贝到屏幕上,于是就完成了UIView的显示。UIView本身不具备显示的功能,显示的是UIView的CALayer。
UIView可以通过subviews属性访问所有的子视图,同样地,CALayer也可以通过sublayers属性访问所有的子层;UIView可以通过superview属性访问父视图,同样地,CALayer也可以通过superlayer属性访问父层。
UIView可以通过addSubview:方法添加子视图,同样地,CALayer可以通过addSublayer:方法添加子层。
在使用时,往往CALayer和UIView都能实现相同的显示效果,但是UIView具有交互的能力,可以处理事件。如果显示出来的东西需要跟用户进行交互的话,那么就要使用用UIView。
3.常用属性
属性 | 说明 | 是否支持隐式动画 |
---|---|---|
anchorPoint | 和中心点position重合的一个点,称为“锚点”,锚点的描述是相对于x、y位置比例而言的默认在图像中心点(0.5,0.5)的位置 | 是 |
backgroundColor | 图层背景颜色 | 是 |
borderColor | 边框颜色 | 是 |
borderWidth | 边框宽度 | 是 |
bounds | 图层大小 | 是 |
contents | 图层显示的内容 | 是 |
contentsRect | 图层显示内容的大小和位置 | 是 |
cornerRadius | 圆角半径 | 是 |
doubleSided | 图层背面是否显示,默认为YES | 否 |
frame | 图层大小和位置 | 否 |
hidden | 是否隐藏 | 是 |
mask | 图层蒙版 | 是 |
maskToBounds | 子图层是否剪切图层边界,默认为NO | 是 |
opacity | 透明度 | 是 |
position | 图层中心点位置 | 是 |
shadowColor | 阴影颜色 | 是 |
shadowOffset | 阴影偏移量 | 是 |
shadowOpacity | 阴影透明度,默认为0,设置阴影必须设置此属性 | 是 |
shadowPath | 阴影的形状 | 是 |
shadowRadius | 阴影模糊半径 | 是 |
sublayers | 子图层 | 是 |
sublayerTransform | 子图层形变 | 是 |
transform | 图层形变 | 是 |
注:
- 在CALayer中很少使用frame属性,因为frame本身不支持隐式动画,通常使用bounds和position代替
- anchorPoint属性是图层的锚点,范围在(01,01)表示在x、y轴的比例,这个点决定着CALayer身上的哪个点会与position所指定的位置重合,当图层中心点固定后,调整anchorPoint即可达到调整图层显示位置的作用(因为它永远和position重合)
4.隐式动画属性
每一个UIView内部都默认关联着一个CALayer,这个CALayer为Root Layer(根层)。对于所有的非Root Layer,即手动创建的CALayer对象,当对非Root Layer的部分属性进行相应的修改时,默认会自动产生一些动画效果,这些属性称为Animatable Properties(可动画属性)。隐式属性动画的本质是这些属性的变动默认隐含了CABasicAnimation动画实现,例如修改bounds属性会产生缩放动画,修改backgroundColor属性会产生背景色的渐变动画,修改position属性会产生平移动画。
可以通过动画事务(CATransaction)关闭默认的隐式动画效果
[CATransactionbegin];
[CATransactionsetDisableActions:YES];
self.myview.layer.position= CGPointMake(100, 100);
[CATransactioncommit];
二、简单使用
1. 设置阴影
imageView.layer.shadowColor = [UIColor grayColor].CGColor; //阴影颜色
imageView.layer.shadowOffset = CGSizeMake(10, 10); //阴影偏移量
imageView.layer.shadowOpacity = 0.5; //阴影透明度
2.设置圆角
imageView.layer.masksToBounds = YES;
imageView.layer.cornerRadius = 10; //当cornerRadius等于边长的一半时,就是圆
3.设置边框
imageView.layer.borderWidth = 5;
imageView.layer.borderColor = [UIColor redColor].CGColor;
4.设置旋转
imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);// 顺时针旋转45
5.绘制图层
绘制图层有两种方法
1.通过图层代理drawLayer: inContext:方法绘制
2.通过自定义图层drawInContext:方法绘制
不管使用哪种方法绘制完必须调用图层的setNeedDisplay方法.
① 使用代理方法绘制图层
通过代理方法进行图层绘图只要指定图层的代理,然后在代理对象中重写-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx方法即可。需要注意这个方法虽然是代理方法但是不用手动实现CALayerDelegate,因为CALayer定义中给NSObject做了分类扩展,所有的NSObject都包含这个方法。另外设置完代理后必须要调用图层的setNeedDisplay方法,否则绘制的内容无法显示。
例
- (void)viewDidLoad {
[super viewDidLoad];
CALayer * layer = [[CALayer alloc]init];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(50, 50);
layer.masksToBounds = YES;
layer.cornerRadius = 50;
layer.borderWidth = 2;
layer.borderColor = [UIColor yellowColor].CGColor;
layer.delegate = self; //设置代理
[self.view.layer addSublayer:layer]; //添加图层
[layer setNeedsDisplay]; //重绘
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 1, -1); //转换坐标系
CGContextTranslateCTM(ctx, 0, -100);
UIImage * image = [UIImage imageNamed:@"me"];
CGContextDrawImage(ctx, CGRectMake(0, 0, 100, 100), image.CGImage);
CGContextRestoreGState(ctx);
}
② 使用自定义图层绘制图层
创建一个CALayer的子类,然后重写drawInContext:方法,在方法中使用Quartz2D API进行绘图;
同样,使用时,必须要调用图层的setNeedDisplay方法,才会触发drawInContext:方法的调用。
Core Animation
一、简介
1.概念
Core Animation 是作用在CALayer上的,使用Core Animation 创建动画不仅简单,而且还有更好的性能,因为Core Animation 是在单独的线程中完成的,不会阻塞主线程,而且只会重绘界面上变化的部分(局部刷新)。
2.分类
类别 | 说明 |
---|---|
CAAnimation | 所有动画类的基类,不能直接使用,实现了CAMediaTiming协议,提供了动画的持续时间、速度和重复计数等,还实现了CAAction协议,该协议为CALayer动画触发的动作提供标准化响应。 |
CATransition | CAAnimation的子类,转场动画,能够为层提供移出屏幕和移入屏幕的动画效果 |
CAAnimationGroup | CAAnimation的子类,动画组,是一种组合模式设计,可以通过动画组来进行所有动画行为的统一控制,组中所有动画效果可以并发执行。 |
CAPropertyAnimation | CAAnimation的子类,属性动画的基类,通过控制可动画属性慢慢地变化,不能直接使用。有两个子类CABasicAnimation 和 CAKeyframeAnimation。 |
CABasicAnimation | CAPropertyAnimation 的子类,基础动画,简单地控制CALayer层的属性慢慢改变,从而实现动画。 |
CAKeyframeAnimation | CAPropertyAnimation 的子类,关键帧动画,同样是通过属性进行动画参数控制,通过values属性指定多个关键帧,通过多个关键帧可以指定动画的各阶段的关键值。 |
基础动画、关键帧动画都属于属性动画,是通过修改属性值产生动画效果,只需要设置初始值和结束值,中间的过程动画(“补间动画”)是由系统自动计算产生。和基础动画不同的是关键帧动画可以设置多个属性值,每两个属性中间的补间动画由系统自动完成,基础动画也可以看成是有两个关键帧的关键帧动画。
二、使用
1. CAAnimation
CAAnimation 是基类,其属性它的子类也拥有。它不能直接使用。
属性 | 说明 |
---|---|
beginTime | 可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间 |
duration | 动画的持续时间 |
delegate | 动画代理 |
repeatCount | 重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT |
repeatDuration | 重复时间 |
removedOnCompletion | 默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,同时还要设置fillMode为kCAFillModeForwards |
fillMode | 决定当前对象在非active时间段的行为。(要想fillMode有效,最好设置removedOnCompletion = NO),kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态;kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态;kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始;kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态 |
timingFunction | 速度控制函数,控制动画运行的节奏,为 CAMediaTimingFunction 类型。kCAMediaTimingFunctionLinear(线性):匀速动画;kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,然后加速离开;kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,然后减速的到达目的地;kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,然后减速的到达目的地。 |
代理方法 : 给NSObject添加了分类,所以任何对象,都可以成为CAAnimation的代理。
动画开始的时候调用 - (void)animationDidStart:(CAAnimation *)anim;
动画停止的时候调用 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
2.CAPropertyAnimation
属性或方法 | 说明 |
---|---|
+ (instancetype)animationWithKeyPath:(nullable NSString *)path; | 类方法,创建一个新的动画对象,同时设置该对象的keyPath属性 |
keyPath | 属性,用来描述动画是作用在哪一个属性上 |
additive | 属性,指定该属性动画是否以当前动画效果为基础 |
cumulative | 属性,指定动画是否为累加效果 |
valueFunction | 该属性值是一个 CAValueFunction 对象,负责对属性改变进行插值计算,系统已经提供了默认的插值计算方式,一般无须指定该属性。 |
位移动画通常使用属性动画控制 CALayer 的 position 属性持续改变,如果要实现 CALayer 的旋转、缩放等动画效果,若在平面上(二维空间)上,则修改 CALayer 的 affineTransform 属性,该属性值为一个CGAffineTransform 对象;若在三维空间上,则修改 CALayer 的 transform 属性,该属性值为一个 CATransform3D 对象。
CATransform3D : 三维变换矩阵
方法 | 说明 |
---|---|
CATransform3DIsIdentity (CATransform3D t) | 返回bool值,判断 t 矩阵是否为单位矩阵 |
CATransform3DEqualToTransform (CATransform3D a, CATransform3D b) | 返回bool值,判断 a , b 两个矩阵是否相等 |
CATransform3DMakeTranslation (CGFloat tx, CGFloat ty, CGFloat tz) | 创建一个在X方向上移动 tx、在Y方向上移动 ty、在Z方向上移动 tz 的变化矩阵 |
CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz) | 创建一个在X方向上缩放 sx、在Y方向上缩放 sy、在Z方向上缩放 sz 的变化矩阵 |
CATransform3DMakeRotation (CGFloat angle, CGFloat x, CGFloat y, CGFloat z) | 创建基于指定旋转轴旋转 angle 弧度的变换,x、y、z 用于确定旋转轴,(1,0,0)指定旋转轴为X轴,(0,1,0)指定旋转轴为Y轴,(0,0,1)指定旋转轴为Z轴 |
CATransform3DTranslate (CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz) | 在 t 变化矩阵的基础上进行位移变换 |
CATransform3DScale (CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz) | 在 t 变化矩阵的基础上进行缩放变换 |
CATransform3DRotate (CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z) | 在 t 变化矩阵的基础上进行旋转变换 |
CATransform3DConcat (CATransform3D a, CATransform3D b) | 将a,b两个矩阵相乘 |
CATransform3DInvert (CATransform3D t) | 对 t 矩形进行反转 |
CATransform3DMakeAffineTransform (CGAffineTransform m) | 将 CGAffineTransform 矩阵转化成 CATransform3D 矩阵,转化后也只有X,Y维度上的变换 |
CATransform3DIsAffine (CATransform3D t) | 返回bool值,判断 t 矩阵是否是 CGAffineTransform 变换矩阵 |
CATransform3DGetAffineTransform (CATransform3D t) | 获取 t 矩阵所包含的 CGAffineTransform 变换矩阵 |
3.CABasicAnimation 和 CAKeyframeAnimation
CAPropertyAnimation的子类,首先创建初始化一个CALayer,初始化动画CABasicAnimation是设置 fromValue 和 toValue ,CAKeyframeAnimation是设置 values ,再设置其他动画属性,将动画添加到图层上。
CALayer 为动画提供的方法
方法 | 说明 |
---|---|
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key | 为 CALayer 添加一个动画,并为动画指定一个唯一标识 |
- (nullable CAAnimation *)animationForKey:(NSString *)key | 让 CALayer 执行 key 所对应的动画 |
- (void)removeAllAnimations | 删除 CALayer 上添加的所有动画 |
- (void)removeAnimationForKey:(NSString *)key | 删除 key 所对应的动画 |
- (nullable NSArray<NSString *> *)animationKeys | 获取 CALayer 上所有动画的 key 组成的数组 |
① 位移动画
CABasicAnimation
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"position"];
anim.fromValue = [NSValue valueWithCGPoint:self.layer.position];
CGPoint toPoint = CGPointMake(300, 300);
anim.toValue = [NSValue valueWithCGPoint:toPoint];
anim.duration = 1.0;
self.layer.position = toPoint;
anim.removedOnCompletion = YES;
[self.layer addAnimation:anim forKey:@"KCBasicAnimation_Position"];
CAKeyframeAnimation
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue * value1 = [NSValue valueWithCGPoint:self.layer.position];
NSValue * value2 = [NSValue valueWithCGPoint:CGPointMake(80, 220)];
NSValue * value3 = [NSValue valueWithCGPoint:CGPointMake(45, 300)];
NSValue * value4 = [NSValue valueWithCGPoint:CGPointMake(55, 400)];
NSArray * values=@[value1,value2,value3,value4];
keyframeAnimation.values = values;
keyframeAnimation.duration = 2.0;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
通过描绘路径进行关键帧动画控制
CAKeyframeAnimation * keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, self.layer.position.x, self.layer.position.y);
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400);
keyframeAnimation.path = path;
CGPathRelease(path);
keyframeAnimation.duration = 4.0;
keyframeAnimation.removedOnCompletion = YES;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
② 旋转动画
CABasicAnimation
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = self.layer.transform;
anim.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D toValue = CATransform3DRotate(fromValue, M_PI, 1, 0, 0);
anim.toValue = [NSValue valueWithCATransform3D:toValue];
anim.duration = 0.5;
self.layer.transform = toValue;
anim.removedOnCompletion = YES;
[self.layer addAnimation:anim forKey:@"KCBasicAnimation_Rotate"];
CAKeyframeAnimation
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue * value1 = [NSValue valueWithCATransform3D:self.layer.transform];
NSValue * value2 = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, 0, 0)];
NSValue * value3 = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 1, 0)];
NSValue * value4 = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
NSArray * values = @[value1,value2,value3,value4];
keyframeAnimation.values = values;
keyframeAnimation.duration = 2.0;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Rotate"];
③ 缩放动画
CABasicAnimation
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = self.layer.transform;
anim.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D toValue = CATransform3DScale(fromValue, 0.5, 0.5, 1);
anim.toValue = [NSValue valueWithCATransform3D:toValue];
anim.duration = 0.5;
self.layer.transform = toValue;
anim.removedOnCompletion = YES;
[self.layer addAnimation:anim forKey:@"KCBasicAnimation_Scale"];
CAKeyframeAnimation
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue * value1 = [NSValue valueWithCATransform3D:self.layer.transform];
NSValue * value2 = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 1)];
NSValue * value3 = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 1)];
NSArray * values = @[value1,value2,value3];
keyframeAnimation.values = values;
keyframeAnimation.duration = 2.0;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Scale"];
④ 动画组
CABasicAnimation
CGPoint fromPoint = self.layer.position;
CGPoint toPoint = CGPointMake(280, fromPoint.y + 300);
CABasicAnimation * moveAnim = [CABasicAnimation animationWithKeyPath:@"position"];
moveAnim.fromValue = [NSValue valueWithCGPoint:fromPoint];
moveAnim.toValue = [NSValue valueWithCGPoint:toPoint];
moveAnim.removedOnCompletion = YES;
CABasicAnimation * transformAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = self.layer.transform;
transformAnim.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D scaleValue = CATransform3DScale(fromValue, 0.5, 0.5, 1);
CATransform3D rotateValue = CATransform3DRotate(fromValue, M_PI, 0, 0, 1);
CATransform3D toValue = CATransform3DConcat(scaleValue, rotateValue);
transformAnim.toValue = [NSValue valueWithCATransform3D:toValue];
transformAnim.cumulative = YES;
transformAnim.repeatCount = 2;
transformAnim.duration = 3;
CAAnimationGroup * animGroup = [CAAnimationGroup animation];
animGroup.animations = @[moveAnim,transformAnim];
animGroup.duration = 6;
[self.layer addAnimation:animGroup forKey:@"KCBasicAnimation_Group"];
CAKeyframeAnimation
CAKeyframeAnimation * moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue * move1 = [NSValue valueWithCGPoint:self.layer.position];
NSValue * move2 = [NSValue valueWithCGPoint:CGPointMake(300, 100)];
NSValue * move3 = [NSValue valueWithCGPoint:CGPointMake(300, 300)];
NSValue * move4 = [NSValue valueWithCGPoint:CGPointMake(100, 300)];
NSValue * move5 = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
NSArray * values = @[move1,move2,move3,move4,move5];
moveAnim.values = values;
moveAnim.removedOnCompletion = YES;
CAKeyframeAnimation * transformAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
CATransform3D rotate1 = self.layer.transform;
CATransform3D rotate2 = CATransform3DMakeRotation(M_PI, 1, 0, 0);
CATransform3D rotate3 = CATransform3DMakeRotation(M_PI, 0, 1, 0);
CATransform3D rotate4 = CATransform3DMakeRotation(M_PI, 0, 0, 1);
CATransform3D rotate5 = CATransform3DMakeRotation(M_PI, 1, 1, 1);
CATransform3D scale1 = self.layer.transform;
CATransform3D scale2 = CATransform3DMakeScale(0.5, 0.5, 1);
CATransform3D scale3 = CATransform3DMakeScale(1, 1, 1);
CATransform3D scale4 = CATransform3DMakeScale(0.5, 0.5, 1);
CATransform3D scale5 = CATransform3DMakeScale(1, 1, 1);
NSValue * transform1 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate1, scale1)];
NSValue * transform2 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate2, scale2)];
NSValue * transform3 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate3, scale3)];
NSValue * transform4 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate4, scale4)];
NSValue * transform5 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate5, scale5)];
transformAnim.values = @[transform1,transform2,transform3,transform4,transform5];
transformAnim.removedOnCompletion = YES;
CAAnimationGroup * animGroup = [CAAnimationGroup animation];
animGroup.animations = @[moveAnim,transformAnim];
animGroup.duration = 8;
self.layer.position = CGPointMake(100, 100);
[self.layer addAnimation:animGroup forKey:@"KCBasicAnimation_Group"];
④ 动画暂停
-(void)pauseLayer{
CFTimeInterval interval=[self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
[self.layer setTimeOffset:interval];
self.layer.speed=0;
}
⑤ 动画恢复
-(void)resumeLayer{
CFTimeInterval beginTime= CACurrentMediaTime() - self.layer.timeOffset;
self.layer.timeOffset = 0;
self.layer.beginTime = beginTime;
self.layer.speed=1.0;
}
4.CATransition
属性 | 说明 |
---|---|
type | 动画过渡效果 |
subtype | 动画过渡方向 |
startProgress | 动画起点(在整体动画的百分比) |
endProgress | 动画终点(在整体动画的百分比) |
CATransition的type属性用于控制动画类型,还有私有动画。
值| 说明 |是否支持方向设置
-----|-----
kCATransitionFade|通过渐隐效果过渡,默认属性值|是
kCATransitionMoveIn|通过移入动画过渡,新视图移到旧视图上面|是
kCATransitionPush|通过推入动画过渡,新视图把旧视图推出去|是
kCATransitionReveal|通过揭开动画过渡,将旧视图移开,同时显示下面的新视图|是
@"cube"|通过立方体旋转动画过渡|是
@"oglFlip"|通过翻转动画过渡|是
@"suckEffect"|通过收缩(吸入)动画过渡|否
@"rippleEffect"|通过水波动画过渡|否
@"pageCurl"|通过向后翻页动画过渡|是
@"pageUnCurl"|通过向前翻页动画过渡|是
@"cameraIrisHollowOpen"|通过相机镜头打开效果动画过渡|否
@"cameraIrisHollowClose"|通过相机镜头关闭效果动画过渡|否
CATransition的subtype属性用于控制动画方向
值 | 说明 |
---|---|
kCATransitionFromRight | 从右侧转场 |
kCATransitionFromLeft | 从左侧转场 |
kCATransitionFromTop | 从顶部转场 |
kCATransitionFromBottom | 从底部转场 |
#import "ViewController.h"
#define IMAGE_COUNT 5
typedef enum {
GestureDirectionUp = 1,
GestureDirectionDown,
GestureDirectionLeft,
GestureDirectionRight
}GestureDirection;
@interface ViewController () <UIPickerViewDelegate,UIPickerViewDataSource>
@property (nonatomic,strong) UIImageView * imageView;
@property (nonatomic,assign) int currentIndex;
@property (nonatomic,strong) CATransition * transition;
@property (nonatomic,strong) UIPickerView * pickerView;
@property (nonatomic,copy) NSArray * transitions;
@property (nonatomic,assign) BOOL isPop;
@end
@implementation ViewController
- (NSArray *)transitions{
if (_transitions == nil) {
_transitions = @[kCATransitionFade,kCATransitionMoveIn,kCATransitionPush,kCATransitionReveal,@"cube",@"oglFlip",@"suckEffect",@"rippleEffect",@"pageCurl",@"pageUnCurl",@"cameralIrisHollowOpen",@"cameraIrisHollowClose"];
}
return _transitions;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
self.navigationItem.title = @"转场";
UIBarButtonItem * rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"效果" style:UIBarButtonItemStyleDone target:self action:@selector(selectTransition:)];
self.navigationItem.rightBarButtonItem = rightBarButtonItem;
self.imageView = [[UIImageView alloc]initWithFrame:self.view.frame];
self.imageView.backgroundColor = [UIColor whiteColor];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.image = [UIImage imageNamed:@"0.jpg"];
[self.view addSubview:self.imageView];
self.pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, screenHeight, screenWidth, 300)];
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
[self.view addSubview:self.pickerView];
UISwipeGestureRecognizer * upSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[upSwipeGesture setDirection:UISwipeGestureRecognizerDirectionUp];
[self.view addGestureRecognizer:upSwipeGesture];
UISwipeGestureRecognizer * downSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[downSwipeGesture setDirection:UISwipeGestureRecognizerDirectionDown];
[self.view addGestureRecognizer:downSwipeGesture];
UISwipeGestureRecognizer * leftSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[leftSwipeGesture setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:leftSwipeGesture];
UISwipeGestureRecognizer * rightSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[rightSwipeGesture setDirection:UISwipeGestureRecognizerDirectionRight];
[self.view addGestureRecognizer:rightSwipeGesture];
self.transition = [[CATransition alloc]init];
self.transition.type = kCATransitionFade;
self.isPop = NO;
}
- (void)selectTransition:(UIBarButtonItem *)btn{
self.isPop = !self.isPop;
if (self.isPop) {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.6];
[self.view bringSubviewToFront:self.pickerView];
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat pickerViewWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat pickerViewHeight = 300;
self.pickerView.frame = CGRectMake(0, screenHeight - pickerViewHeight, pickerViewWidth, pickerViewHeight);
[UIView commitAnimations];
}
else {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.6];
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat pickerViewWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat pickerViewHeight = 300;
self.pickerView.frame = CGRectMake(0, screenHeight, pickerViewWidth, pickerViewHeight);
[UIView commitAnimations];
}
}
-(void)swipe:(UISwipeGestureRecognizer *)gesture{
if (gesture.direction == UISwipeGestureRecognizerDirectionUp){
[self transitionAnimation:GestureDirectionUp];
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionDown){
[self transitionAnimation:GestureDirectionDown];
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft){
[self transitionAnimation:GestureDirectionLeft];
}
else {
[self transitionAnimation:GestureDirectionRight];
}
}
- (void)transitionAnimation:(GestureDirection)gestureDirection{
switch (gestureDirection) {
case GestureDirectionUp:
self.transition.subtype = kCATransitionFromTop;
break;
case GestureDirectionDown:
self.transition.subtype = kCATransitionFromBottom;
break;
case GestureDirectionLeft:
self.transition.subtype = kCATransitionFromRight;
break;
case GestureDirectionRight:
self.transition.subtype = kCATransitionFromLeft;
break;
default:
break;
}
self.transition.duration = 1.0f;
self.imageView.image = [self getImage];
[self.view.layer addAnimation:self.transition forKey:@"KCTransitionAnimation"];
}
- (UIImage *)getImage{
self.currentIndex = (self.currentIndex + 1) % IMAGE_COUNT;
NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
return [UIImage imageNamed:imageName];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return self.transitions.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return self.transitions[row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
self.transition.type = self.transitions[row];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
5.逐帧动画
通过设置UIImageView的animationImages属性,然后调用它的startAnimating方法去播放一组逐帧图片,但是它存在着很大的性能问题,并且这种方法一旦设置完图片中间的过程就无法控制了。使用CADisplayLink加入到主循环队列中,循环调用目标方法,在这个方法中更新视图内容就可以完成逐帧动画。
- (void)runAnimationWithImageCount:(NSInteger)count AndImageName:(NSString *)name
{
if (self.myImageView.isAnimating)
{
return;
}
NSMutableArray * pictArray = [NSMutableArray array];
for (int i = 0; i < count; i++)
{
NSString * pictNum = [NSString stringWithFormat:@"%@_%02d.jpg",name,i];
// 使用imageNamed的方式加载图片会有缓存,使得占用的内存很大
//[pictArray addObject:[UIImage imageNamed:pictNum]];
// 直接从文件中读取图片就不会占用太大的内存
NSBundle * boudle = [NSBundle mainBundle];
NSString * path = [boudle pathForResource:pictNum ofType:nil];
[pictArray addObject:[UIImage imageWithContentsOfFile:path]];
}
self.myImageView.animationImages = pictArray;
self.myImageView.animationDuration = (self.myImageView.animationImages.count * 0.07);
CGFloat delayTime = self.myImageView.animationDuration + 0.5;
self.myImageView.animationRepeatCount = 1;
[self.myImageView startAnimating];
[self.myImageView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:delayTime];
}
6.UIView动画封装
UIView本身对于基本动画和关键帧动画、转场动画都有相应的封装。
方法 | 说明 |
---|---|
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context | 开始动画,animationID :动画块内部应用程序标识,context :附加的信息,都传递给动画代理 |
+ (void)commitAnimations | 提交动画,结束动画 |
+ (void)setAnimationDelegate:(nullable id)delegate | 设置动画的代理 |
+ (void)setAnimationWillStartSelector:(nullable SEL)selector | 设置动画开始时的执行方法 |
+ (void)setAnimationDidStopSelector:(nullable SEL)selector | 设置动画结束时的执行方法 |
+ (void)setAnimationDuration:(NSTimeInterval)duration | 设置动画执行时间 |
+ (void)setAnimationDelay:(NSTimeInterval)delay | 设置动画延迟执行的时间 |
+ (void)setAnimationStartDate:(NSDate *)startDate | 设置动画开始执行的时间 |
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve | 设置动画运行过程中相对的速度 |
+ (void)setAnimationRepeatCount:(float)repeatCount | 设置动画重复次数 |
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses | 设置是否自动逆向动画 |
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState | 设置动画是否从当前状态开始执行,设置为YES,则当上一次动画正在执行中,那么当下一个动画开始时,上一次动画的当前状态将成为下一次动画的开始状态。设置为NO,则当上一个动画正在执行中,那么当下一个动画开始时,上一次动画需要先恢复到完成时的状态,然后在开始执行下一次动画 |
+ (void)setAnimationsEnabled:(BOOL)enabled | 设置动画是否可用 |
+ (BOOL)areAnimationsEnabled | 获取动画是否可用 |
动画使用block方式
方法 | 说明 |
---|---|
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations | 使用block方式 |
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 使用block方式,有完成后的block |
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 使用block方式,可以设置动画延迟时间和动画参数 |
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 转场动画 |
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion | 转场动画,toView添加到fromView的父视图中,fromView从它的福视图中移除 |
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 使用block方式实现关键帧动画 |
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations | 从开始时间添加一个持续时间的动画 |
UIViewAnimationCurve
值 | 说明 |
---|---|
UIViewAnimationCurveEaseIn | 先慢后快,在动画刚开始的时候执行速度慢 |
UIViewAnimationCurveEaseOut | 先快后慢,在动画快结束的时候执行速度慢 |
UIViewAnimationCurveEaseInOut | 先由慢变快,然后再变慢,在动画刚开始和快结束的时候速度慢 |
UIViewAnimationCurveLinear | 匀速运行 |
UIViewAnimationOptions
值 | 说明 |
---|---|
UIViewAnimationOptionLayoutSubviews | 动画过程中保证子视图跟随运动 |
UIViewAnimationOptionAllowUserInteraction | 动画过程中允许用户交互 |
UIViewAnimationOptionBeginFromCurrentState | 所有视图从当前状态开始运行 |
UIViewAnimationOptionRepeat | 重复执行动画 |
UIViewAnimationOptionAutoreverse | 逆向动画,动画运行到结束点后仍然以动画方式回到初始点 |
UIViewAnimationOptionOverrideInheritedDuration | 忽略嵌套动画时间设置 |
UIViewAnimationOptionOverrideInheritedCurve | 忽略嵌套动画速度设置 |
UIViewAnimationOptionAllowAnimatedContent | 动画过程允许重绘视图(仅适用在转场动画中) |
UIViewAnimationOptionShowHideTransitionViews | 视图切换时翻转隐藏旧视图、显示新视图,而不再是直接删除旧视图、添加新视图 |
UIViewAnimationOptionOverrideInheritedOptions | 不继承任何参数或动画类型 |
UIViewAnimationOptionCurveEaseInOut | 默认值,和UIViewAnimationCurveEaseInOut效果一样,只是这个值用于block动画参数,先由慢变快,然后再变慢,在动画刚开始和快结束的时候速度慢 |
UIViewAnimationOptionCurveEaseIn | 和UIViewAnimationCurveEaseIn效果一样,先慢后快,在动画刚开始的时候执行速度慢 |
UIViewAnimationOptionCurveEaseOut | 和UIViewAnimationCurveEaseOut效果一样,先快后慢,在动画快结束的时候执行速度慢 |
UIViewAnimationOptionCurveLinear | 和UIViewAnimationCurveLinear效果一样,匀速运行 |
UIViewAnimationOptionTransitionNone | 下面的值仅适用于转场动画设置,没有转场动画效果 |
UIViewAnimationOptionTransitionFlipFromLeft | 从左侧翻转 |
UIViewAnimationOptionTransitionFlipFromRight | 从右侧翻转 |
UIViewAnimationOptionTransitionCurlUp | 向后翻页的动画过渡 |
UIViewAnimationOptionTransitionCrossDissolve | 水波动画过渡 |
UIViewAnimationOptionTransitionFlipFromTop | 从顶部翻转 |
UIViewAnimationOptionTransitionFlipFromBottom | 从底部翻转 |
UIViewKeyframeAnimationOptions
其中一部分值和 UIViewAnimationOptions 是一样的,不一样的如下:
值 | 说明 |
---|---|
UIViewKeyframeAnimationOptionCalculationModeLinear | 连续运算模式 |
UIViewKeyframeAnimationOptionCalculationModeDiscrete | 离散运算模式 |
UIViewKeyframeAnimationOptionCalculationModePaced | 均匀执行运算模式 |
UIViewKeyframeAnimationOptionCalculationModeCubic | 平滑运算模式 |
UIViewKeyframeAnimationOptionCalculationModeCubicPaced | 平滑均匀运算模式 |
7.弹簧动画
方法 | 说明 |
---|---|
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | UIViewAnimationWithBlocks中的方法,用来创建具有弹簧效果的动画。dampingRatio为阻尼,范围0~1,阻尼越接近于0,弹性效果越明显。velocity为弹性复位的速度。 |
[UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
self.imageView.center = CGPointMake(300, 300);
} completion:nil];