使用方法animationWithKeyPath:
对 CABasicAnimation进行实例化,并指定Layer的属性做为关键路径进行注册。缓存
//围绕y轴旋转 CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
设定动画的属性和说明动画
属性 | 说明 |
---|---|
duration | 动画的时长 |
repeatCount | 重复的次数。不停重复设置为 HUGE_VALF |
repeatDuration | 设置动画的时间。在该时间内动画一直执行,不计次数。 |
beginTime | 指定动画开始的时间。从开始延迟几秒的话,设置为【CACurrentMediaTime() + 秒数】 的方式 |
timingFunction | 设置动画的速度变化 |
autoreverses | 动画结束时是否执行逆动画 |
fromValue | 所改变属性的起始值 |
toValue | 所改变属性的结束时的值 |
byValue | 所改变属性相同起始值的改变量 |
transformAnima.fromValue = @(M_PI_2); transformAnima.toValue = @(M_PI); transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transformAnima.autoreverses = YES; transformAnima.repeatCount = HUGE_VALF; transformAnima.beginTime = CACurrentMediaTime() + 2;
只需设置removedOnCompletion
、fillMode
两个属性就能够了。3d
transformAnima.removedOnCompletion = NO; transformAnima.fillMode = kCAFillModeForwards;
解释:为何动画结束后返回原状态?
首先咱们须要搞明白一点的是,layer动画运行的过程是怎样的?其实在咱们给一个视图添加layer动画时,真正移动并非咱们的视图自己,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为何咱们的视图在动画结束后又回到了原来的状态,由于它根本就没动过。代理
这个一样也能够解释为何在动画移动过程当中,咱们为什么不能对其进行任何操做。code
因此在咱们完成layer动画以后,最好将咱们的layer属性设置为咱们最终状态的属性,而后将presentation layer 移除掉。orm
[self.imageView.layer addAnimation:transformAnima forKey:@"A"];
须要注意的两点对象
addAnimation:forKey:
是将 CABasicAniamtion 对象进行了 copy 操做的。因此在将其添加到一个layer上以后,咱们仍是将其再次添加到另外一个layer上的。该属性定义了你的动画在开始和结束时的动做。默认值是 kCAFillModeRemoved
。blog
取值的解释事件
也便是属性timingFunction
值的设定,有种方式来获取属性值图片
functionWithName:
这种方式很简单,这里只是简单说明一下取值的含义:
kCAMediaTimingFunctionLinear 传这个值,在整个动画时间内动画都是以一个相同的速度来改变。也就是匀速运动。
kCAMediaTimingFunctionEaseIn 使用该值,动画开始时会较慢,以后动画会加速。
kCAMediaTimingFunctionEaseOut 使用该值,动画在开始时会较快,以后动画速度减慢。
kCAMediaTimingFunctionEaseInEaseOut 使用该值,动画在开始和结束时速度较慢,中间时间段内速度较快。
functionWithControlPoints: : : :
实现,这个以后再说,占个坑先。removedOnCompletion
属性为NOfillMode
尽可能取默认值就行了,不要去设置它的值。只有在极个别的状况下咱们会修改它的值,之后会说到,这里先占个坑。直接上代码
CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"]; positionAnima.duration = 0.8; positionAnima.fromValue = @(self.imageView.center.y); positionAnima.toValue = @(self.imageView.center.y-30); positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; positionAnima.repeatCount = HUGE_VALF; positionAnima.repeatDuration = 2; positionAnima.removedOnCompletion = NO; positionAnima.fillMode = kCAFillModeForwards; [self.imageView.layer addAnimation:positionAnima forKey:@"AnimationMoveY"];
CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"]; positionAnima.fromValue = @(self.imageView.center.y); positionAnima.toValue = @(self.imageView.center.y-30); positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; transformAnima.fromValue = @(0); transformAnima.toValue = @(M_PI); transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; CAAnimationGroup *animaGroup = [CAAnimationGroup animation]; animaGroup.duration = 2.0f; animaGroup.fillMode = kCAFillModeForwards; animaGroup.removedOnCompletion = NO; animaGroup.animations = @[positionAnima,transformAnima]; [self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
为了获取动画的开始和结束事件,须要实现协议
positionAnima.delegate = self;
代理方法实现
//动画开始时 - (void)animationDidStart:(CAAnimation *)anim { NSLog(@"开始了"); } //动画结束时 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { //方法中的flag参数代表了动画是天然结束仍是被打断,好比调用了removeAnimationForKey:方法或removeAnimationForKey方法,flag为NO,若是是正常结束,flag为YES。 NSLog(@"结束了"); }
其实比较重要的是有多个动画的时候如何在代理方法中区分不一样的动画
两种方式
若是咱们添加动画的视图是全局变量,可以使用该方法。
添加动画时,咱们使用了
[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];
因此,可根据key来区分不一样的动画
//动画开始时 - (void)animationDidStart:(CAAnimation *)anim { if ([anim isEqual:[self.imageView.layer animationForKey:@"Animation"]]) { NSLog(@"动画组执行了"); } }
Note:把动画存储为一个属性而后再回调中比较,用来断定是哪一个动画是不可行的。应为委托传入的动画参数是原始值的一个深拷贝,不是同一个值
添加动画的视图是局部变量时,可以使用该方法
添加动画给动画设置key-value对
[positionAnima setValue:@"PositionAnima" forKey:@"AnimationKey"]; [transformAnima setValue:@"TransformAnima" forKey:@"AnimationKey"];
因此,能够根据key中不一样的值来进行区分不一样的动画
//动画结束时 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"PositionAnima"]) { NSLog(@"位置移动动画执行结束"); } else if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"TransformAnima"]){ NSLog(@"旋转动画执行结束"); } }
因为CAAnimation的delegate使用的strong类型,
因此在全局变量以下设置时会产生循环引用的状况
self.animation.delegate = self;//可经过复用dealloc方法来验证
解决方案
//.h #import <UIKit/UIKit.h> @interface AnimationDelegate : NSObject @end //.m #import "AnimationDelegate.h" @implementation AnimationDelegate - (void)animationDidStart:(CAAnimation *)anim { NSLog(@"Animation Start"); } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { NSLog(@"Animation Stop"); } - (void)dealloc { NSLog(@"Delegate Dealloc"); } @end
使用方式
self.animation.delegate = [[AnimationDelegate alloc]init];
NSProxy
来解决YYWeakProxy
类self.animation.delegate = [YYWeakProxy proxyWithTarget:self];
值 | 说明 | 使用形式 |
---|---|---|
transform.scale | 比例转化 | @(0.8) |
transform.scale.x | 宽的比例 | @(0.8) |
transform.scale.y | 高的比例 | @(0.8) |
transform.rotation.x | 围绕x轴旋转 | @(M_PI) |
transform.rotation.y | 围绕y轴旋转 | @(M_PI) |
transform.rotation.z | 围绕z轴旋转 | @(M_PI) |
cornerRadius | 圆角的设置 | @(50) |
backgroundColor | 背景颜色的变化 | (id)[UIColor purpleColor].CGColor |
bounds | 大小,中心不变 | [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)]; |
position | 位置(中心点的改变) | [NSValue valueWithCGPoint:CGPointMake(300, 300)]; |
contents | 内容,好比UIImageView的图片 | imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage; |
opacity | 透明度 | @(0.7) |
contentsRect.size.width | 横向拉伸缩放 | @(0.4)最好是0~1之间的 |