上一篇,我们学习了CAShapeLayer+UIBezierPath如何去画图形,那画了图形之后,如果我们想要对自己画的图形进行一些基础动画操作,那怎么去做呢?CABasicAnimation就能满足你。
1. CABasicAnimation
CABasicAnimation是继承于CAAnimation,说明了CABasicAnimation只是动画的实现方式之一。
那谁能用的上呢,这里直接给出答案CALayer。
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key;
CALayer的方法,可以添加动画。
闲话就不说了,我们先来看下CABasicAnimation有哪些属性?
-
fromValue: 属性变化的初始值,默认为0。
-
toValue: 属性变化的结束值,这个值是和keyPath有关。
-
byValue: 属性变化的值。
-
duration: 动画时长。
-
speed: 速度,如果为2,则时长会减半。
-
repeatCount:重复次数,MAXFLOAT则为重复进行。
-
autoreverses:动画执行完后是否逆动画,一般都设置为NO。
-
fillMode:动画结束后的状态。Removed为原始状态。Forwards为最后状态,要配和removedOnCompletion为NO实现。
-
timingFunction:动画速度的变化,Linear为匀速,EaseInOut为头尾慢,中间快,EaseIn为开始慢,接着加速,EaseOut就是开始加速,最后慢。
-
removedOnCompletion:执行动画后要不要删除动画。设置为NO则切换后台或者控制器之间,动画不会消失。
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) { UIViewAnimationCurveEaseInOut, // slow at beginning and end UIViewAnimationCurveEaseIn, // slow at beginning UIViewAnimationCurveEaseOut, // slow at end UIViewAnimationCurveLinear, };
代码里面也有简单注释 UIViewAnimationCurve 的用法。
2. keyPath常用的值
+ (instancetype)animationWithKeyPath:(nullable NSString *)path;
我们要创建动画,是为谁创建动画了呢,看初始化,是传一个path的属性,那这个是传什么呢,记住啦,就是传CALayer的相关属性,可以是系统的,也可以是继承CALayer的属性。
下面将列一下常用的属性。
-
transform.rotation.z: 围绕z轴变化,toValue就传2π。
-
transform.scale:缩放的比例,1为不变。
-
backgroundColor:背景颜色的变化
-
bounds:视图的大小变化,position是不变的。
-
position:视图中心点的变化。
-
contents:内容的变化。
-
opacity:透明度的变化。
差不都就这些了,简单就够用了。
3. delegate 引用问题
动画中我们也可以用 delegate 来回调动画完成情况。
我们实现代理 CAAnimationDelegate。
/* 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 <CAAnimationDelegate> delegate;
在这里,我们可以看到 delegate 是强引用。所以为了避免循环引用问题。我们需要在动画释放之后删除动画。
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
//对应添加的key即可。
[_animationView.layer removeAnimationForKey:@"transform.translation.z"];
}
那如果动画结束之前,就直接返回了,这时候也会出现内存泄露问题。所以我们还可以通过中间管理来修饰。
@protocol JJAnimationDelegate <NSObject>
@optional
- (void)animationDidStart:(CAAnimation *)anim;
- (void)animationDidStop:(CAAnimation *)anim;
@end
@interface JJAnimationDelegateManager : NSObject <CAAnimationDelegate>
@property (nonatomic, weak) id<JJAnimationDelegate> delegate;
@end
@implementation AnimationDelegateManager
- (void)animationDidStart:(CAAnimation *)anim {
if (_delegate && [_delegate respondsToSelector:@selector(animationDidStart:)]) {
[_delegate animationDidStart:anim];
}
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
if (_delegate && [_delegate respondsToSelector:@selector(animationDidStop:)]) {
[_delegate animationDidStop:anim];
}
}
@end
调用方式直接用
JJAnimationDelegateManger *manager = [JJAnimationDelegateManger new]; manager.delegate = self; animation.delegate = manager.delegate;
4. 例子
又到了上代码的时候了,这次我们就做一个圈圈,圈圈旋转的动画,圈圈的4分之三是一种颜色,4分之一是一种颜色。
大概和这个图差不多吧,然后旋转起来。
那我们就利用上一次学的CAShapeLayer+UIBezierPath,把这个图先画出来先。
CGRect rect = CGRectMake(0, 0, 50, 50);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];
// 设置路径画布
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.lineWidth = 4.0;
lineLayer.strokeColor = [UIColor yellowColor].CGColor;
lineLayer.strokeStart = 0;
lineLayer.strokeEnd = 0.75;
lineLayer.path = path.CGPath;
lineLayer.fillColor = [UIColor clearColor].CGColor;
[view.layer addSublayer:lineLayer];
CAShapeLayer *lineLayer2 = [CAShapeLayer layer];
lineLayer2.lineWidth = 4.0;
lineLayer2.strokeColor = [UIColor systemPinkColor].CGColor;
lineLayer2.strokeStart = 0.75;
lineLayer2.strokeEnd = 1;
lineLayer2.path = path.CGPath;
lineLayer2.fillColor = [UIColor clearColor].CGColor;
[view.layer addSublayer:lineLayer2];
然后我们再添加上动画效果,这里就用transform.rotation.z为keypath做好。
CABasicAnimation *basicAni = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
basicAni.toValue = @(M_PI*2);
basicAni.duration = 1.0;
basicAni.repeatCount = MAXFLOAT;
basicAni.autoreverses = false;
basicAni.fillMode = kCAFillModeForwards;
[view.layer addAnimation:basicAni forKey:nil];
大功告成,我们看一下效果图。
还不错,那为什么有蓝色呢,去掉背景蓝色就是和上面的图片一样样的了。
到了这里,可能大家又会想,这里只能一种动画,能不能多种进行呢。答案是可以的。
我们用CAAnimationGroup就组合就行。
CABasicAnimation *basicAni = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
basicAni.toValue = @(M_PI*2);
basicAni.duration = 1.0;
basicAni.repeatCount = MAXFLOAT;
basicAni.autoreverses = false;
basicAni.fillMode = kCAFillModeForwards;
CABasicAnimation *basicAni2 = [CABasicAnimation animationWithKeyPath:@"position.y"];
basicAni2.toValue = @(view.center.y + 100);
basicAni2.duration = 2.0;
basicAni2.repeatCount = MAXFLOAT;
basicAni2.fillMode = kCAFillModeForwards;
CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[basicAni, basicAni2];
animationGroup.repeatCount = MAXFLOAT;
animationGroup.duration = 2;
[view.layer addAnimation:animationGroup forKey:nil];
看效果
气泡浮动动画
注意:要用 centerY 来进行上下浮动,点击的时候位置才不会偏离。
[self.layer removeAllAnimations];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
animation.fromValue = @(self.centerY);
animation.toValue = @(self.centerY + arc4random()%5 + 3);
animation.duration = arc4random()%3 + 1;
animation.repeatCount = CGFLOAT_MAX;
animation.autoreverses = YES;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.layer addAnimation:animation forKey:@"basic"];
5. 总结
CABasicAnimation到这里也就差不多了,总结起来有句话就是指定可动画属性后,动画会按照预定的参数持续一定时间由初始值变换为终点值。
很多同学可能心里会想着,这里都只有toValue,fromValue,那如果想要搞多种效果呢,有的,不过是关键帧动画CAKeyframeAnimation,这个下期我们再详细的看看,到时候再看看这些动画又有什么不同呢。