CABasicAnimation
本章主要讲解CABasicAnimation的一些属性和用CABasicAnimation实现的一些动画
property | 解释 |
---|---|
fromValue | 所改变属性的起始值 |
toValue | 所改变属性的结束时的值 |
byValue | 所改变属性相同起始值的改变量 |
/*
* 支持以下设置值的方式
* 1、当 fromValue 和 toValue不为空;动画就是从 fromValue状态 到 toValue状态
*
* 2、当 fromValue 和 byValue不为空;动画就是从 fromValue状态 到 (fromValue+toValue)状态
*
* 3、当 byValue 和 toValue不为空;动画就是从 (toValue-byValue)状态 到 toValue 状态
*
* 4、只有fromValue 不为空;动画就是从 fromValue 到 属性当前呈现的value状态
*
* 5、只有toValue不为空;动画就是从layer在渲染树中属性的当前value状态到 toValue 状态
*
* 6、只有byValue不为空;动画就是从layer在渲染树中属性的当前value状态到 (value+byValue) 状态
*/
@property(nullable, strong) id fromValue;
@property(nullable, strong) id toValue;
@property(nullable, strong) id byValue;
附:如何让动画结束后保持在动画结束状态
anima.removedOnCompletion = NO;
anima.fillMode = kCAFillModeForwards;
解释:为什么动画结束后返回原状态?
首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。
参考:https://www.jianshu.com/p/02c341c748f9#
继续:
用CABasicAnimation实现一些移动、旋转、缩放
说明:self.animView 是一个矩形显示框,self.animView.layer.archorPoint 默认是(0.5, 0.5)
一、移动
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CABasicAnimation *tranferAnim = [CABasicAnimation animationWithKeyPath:@"position.x"];
tranferAnim.fromValue = [NSNumber numberWithDouble:width/2];
tranferAnim.toValue = [NSNumber numberWithDouble:width-40];
//渐进动画
tranferAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
//保持动画运行结束的状态
tranferAnim.removedOnCompletion = NO;
tranferAnim.fillMode = kCAFillModeForwards;
[self.animView.layer addAnimation:tranferAnim forKey:nil];
二、旋转
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
animation.fromValue = @(0);
animation.toValue = @(-M_PI);
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animation.repeatCount = 1;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[self.animView.layer addAnimation:animation forKey:nil];
三、缩放
//Y 方向放大2.5倍
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
animation.fromValue = @(1);
animation.byValue = @(1.5);
animation.delegate = self;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
animation.repeatCount = 1;
animation.removedOnCompletion = NO;
animation.fillMode = kCAFillModeForwards;
[self.animView.layer addAnimation:animation forKey:nil];
说明:针对Y方向的缩放是根据锚点向上下两个方向缩放的,如果只
想让其在一个方向缩放,可以通过更改layer的锚点实现;
向下缩放:可设置锚点(x, 0)
向上缩放:可设置锚点(x, 1)
四、- (void)addAnimation: forKey: 方法理解
/* 给layer层关联一个动画操作
*
* 1、'key'可以使任意字符,但是一个’key‘只能对应一个动画,nil 也是一个有效的key;
*
* 2、一个特殊的’key‘ ='transition' 被用来指定是转场动画;
*
* 3、如果动画的“持续时间”属性为零或负值,则会给出默认的持续时间,
* 要么是“animationDuration”事务属性的值,要么是.25秒。
*
* 4、传入到 layer的animation是anim的一个深copy,因此把动画添加到layer之后,再更改animation的属性不会对layer动画有所影响;*/
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
思考
1、key = nil 与 key !=nil 的区别
layer中维护了一个私有属性animations,该属性是数组类型,当设置key=nil时,
[self.animView.layer addAnimation:animation forKey:nil];
animations = [
当设置key!=nil时,
[self.animView.layer addAnimation:animation forKey:@"scaleY"];
animations = [scaleY=
此时可以通过layer的方法获取CAAnimation对象
CAAnimation * anim = [self.animView.layer animationForKey:@"scaleY"]
一般会应用在CAAniamtionDelegate中,判断是哪个动画开始执行,哪个动画执行完成;
- (void)animationDidStart:(CAAnimation *)anim {
if ([anim isEqual:[self.animView.layer animationForKey:@"scaleY"]]) {
NSLog(@"缩放动画开始执行");
} else if ([anim isEqual:[self.animView.layer animationForKey:@"rotateY"]]) {
NSLog(@"旋转动画开始执行");
}
}
2、传入到 layer的animation是anim的一个深copy,这个我们改如何验证呢?
首先对比传入前的动画对象和回调函数中的动画对象的地址就可以看出来;如下图
再者,可以通过animationForKey:方法获取到layer中的动画对象
CABasicAnimation *animationInLayer = [self.animView.layer animationForKey:@"scaleY"];
NSLog(@"%@", animationInLayer);
附:看苹果对animationForKey方法的解释,通过这个方法返回的CAAnimation对象时只读的,因此不能修改对象已设置动画的任何属性
/* Returns the animation added to the layer with identifier 'key', or nil
* if no such animation exists. Attempting to modify any properties of
* the returned object will result in undefined behavior. */
- (nullable __kindof CAAnimation *)animationForKey:(NSString *)key;
3、为什么这里- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
anim要设置成深拷贝呢?
anim对象关联到layer后,为了防止动画过程中改变动画属性的只,导致不可预测的问题,深拷贝后的只读对象保证了不能任意改变动画的属性。
4、CAAnimation 导致的内存泄漏问题?
/* The delegate of the animation. This object is retained for the
* lifetime of the animation object. Defaults to nil. See below for the
* supported delegate methods. */
@property(nullable, strong) id delegate;
/* When true, the animation is removed from the render tree once its
* active duration has passed. Defaults to YES. */
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;
注意这里delegate是strong属性,在默认情况下动画运行结束后会从渲染树中移除,但是当removedOnCompletion设置为NO时,动画结束后不会移除,导致内存泄漏,此时需要在动画运行结束后,主动把动画移除,以解除内存泄漏;
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
[self.view.layer removeAllAnimations];
}