iOS 类似亲宝宝app下拉刷新动画效果,最近看了下这种效果,感觉有点意思。于是就实现了一下。
方案一
采用两个背景View1、View2,三个球ball1,ball2,ball3,将ball1,ball2放到View1上,ball3放到View2上。
当动画开始的时候,开始旋转View1,当动画结束后,将ball2放到View2,接着旋转View2,依次循环。
旋转代码
- (CAAnimation *)rotateAnimation {
//初始化一个动画
CABasicAnimation *animation = [CABasicAnimation animation];
//动画运动的方式,现在指定的是围绕Z轴旋转
animation.keyPath = @"transform.rotation.z";
//动画持续时间
animation.duration = 0.35;
//开始的角度
animation.fromValue = [NSNumber numberWithFloat:0];
//结束的角度
animation.toValue = [NSNumber numberWithFloat:M_PI];
//动画的运动方式
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
//是否反向移动动画
// animation.autoreverses = YES;
animation.repeatCount = 1;
//动画的代理
animation.delegate = self;
//动画结束后的状态
//animation.fillMode = kCAFillModeBackwards;
animation.removedOnCompletion = YES;
return animation;
}
当动画结束后,实现代理animationDidStop方法
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
CAAnimation *rotate1Animate = [self.content1ImageView.layer animationForKey:@"rotate"];
CAAnimation *rotate2Animate = [self.content2ImageView.layer animationForKey:@"rotate"];
if (!(rotate1Animate == anim || rotate2Animate == anim)) {
return;
}
if (flag) {
if (self.animationStoped) {
return;
}
if (self.rotateIndex == 1) {
self.rotateIndex = 0;
} else {
self.rotateIndex = 1;
}
[self redisplayBall];
[self loadAnimation];
}
}
- (void)redisplayBall {
// 1-0-2
if (self.rotateIndex == 0) {
// 旋转第一个控件
[self.content1ImageView addSubview:self.aballImageView];
[self.content1ImageView addSubview:self.bballImageView];
[self.content2ImageView addSubview:self.cballImageView];
} else {
// 旋转第二个控件
[self.content1ImageView addSubview:self.aballImageView];
[self.content2ImageView addSubview:self.bballImageView];
[self.content2ImageView addSubview:self.cballImageView];
}
[self layoutBallFrame];
}
// 重新加载动画,效果,依次旋转View1与View2
- (void)loadAnimation {
if (self.rotateIndex == 0) {
[self.content1ImageView.layer addAnimation:[self rotateAnimation] forKey:@"rotate"];
} else {
[self.content2ImageView.layer addAnimation:[self rotateAnimation] forKey:@"rotate"];
}
}
最终实现该效果。
方案二
采用路径动画来实现,三个球aballImageView,bballImageView,cballImageView,三个球的位置a-c-b。还有四个path,及四个半圆的path,上边两个top1Path、top2Path,下边两个bottom1Path、bottom2Path。
当动画开始是,球aballImageView按照top1Path的路径进行动画;同时球cballImageView按照bottom1Path的路径进行动画;当这次动画结束后,球cballImageView按照top2Path的路径进行动画,同时球bballImageView按照bottom2Path的路径进行动画;依次循环。
- (void)loadAnimation {
if (!self.hasRotated) {
// 如果是球aballImageView、cballImageView
[self.aballImageView.layer addAnimation:[self rotateAnimation:self.top1Path] forKey:@"showPathAnimation"];
[self.cballImageView.layer addAnimation:[self rotateAnimation:self.bottom1Path] forKey:@"showPathAnimation"];
} else {
// 如果是球cballImageView、bballImageView
[self.cballImageView.layer addAnimation:[self rotateAnimation:self.top2Path] forKey:@"showPathAnimation"];
[self.bballImageView.layer addAnimation:[self rotateAnimation:self.bottom2Path] forKey:@"showPathAnimation"];
}
}
路径动画
- (CAAnimation *)rotateAnimation:(UIBezierPath *)path {
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
// 动画时间间隔
animation.duration = 0.35f;
// 重复次数为最大值
animation.repeatCount = 1;
animation.removedOnCompletion = NO;
// animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.calculationMode = kCAAnimationPaced;
animation.delegate = self;
animation.path = path.CGPath;
return animation;
}
动画结束后,重新开始动画
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
CAAnimation *aBallAnimate = [self.aballImageView.layer animationForKey:@"showPathAnimation"];
CAAnimation *bBallAnimate = [self.bballImageView.layer animationForKey:@"showPathAnimation"];
CAAnimation *cBallAnimate = [self.cballImageView.layer animationForKey:@"showPathAnimation"];
if (!(aBallAnimate == anim || bBallAnimate == anim || cBallAnimate == anim)) {
return;
}
if (flag) {
if (self.animationStoped) {
return;
}
if (self.hasRotated) {
self.hasRotated = NO;
[self.bballImageView.layer removeAnimationForKey:@"showPathAnimation"];
} else {
self.hasRotated = YES;
[self.aballImageView.layer removeAnimationForKey:@"showPathAnimation"];
}
[self loadAnimation];
}
}
注意点:
当使用CAKeyframeAnimation动画时,我们需要设置animation.fillMode = kCAFillModeRemoved;
动画结束后,回到原先状态,这样的,三个球的位置依然是a-c-b,这个时候开始后面的动画就可以对c、b球进行动画。
fillMode可以的值
定义定时对象在其活动持续时间之外的行为方式,本地时间可以限制在活动持续时间的任何一端,或者可以从演示文稿中删除元素。值分别是“后退”、“前进”、“两者”和“删除”。默认为移除
大意:fillMode的作用就是决定当前对象过了非active时间段的行为. 比如动画开始之前,动画结束之后
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 这个和kCAFillModeForwards是相对的,就是在动画开始前,你只要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始.你可以这样设定测试代码,将一个动画加入一个layer的时候延迟5秒执行.然后就会发现在动画没有开始的时候,只要动画被加入了layer,layer便处于动画初始状态
kCAFillModeBoth 理解了上面两个,这个就很好理解了,这个其实就是上面两个的合成.动画
所以默认就是kCAFillModeRemoved,恢复动画开始前的状态。