最近刚好需要用到一些动画效果,所以对CoreAnimation 进行了一些研究,在使用过程中,也有产生一些疑问,在此和大家分享。
本文主要是展示对CoreAnimation 的快速使用,多种动画的集合,每一个动画放在一个独立的VC中,清晰的代码,也有购物车动画,转场动画,弹簧动画等等。
CAAnimation:核心动画的基类,由属性timingFunction控制动画运行的速度变化,由duration 控制动画持续时间。
CAPropertyAnimation:属性动画的基类。
CAAnimationGroup:动画组,可以将多个动画组合,并行一起执行的一个类。
CATransition:转场动画,在切换一些视图,可以产生较炫丽的动画效果。
CABasicAnimation:基础动画,属性动画,可以直接使用,一般是较简单的动画。
CAKeyframeAnimation:关键帧动画,属性动画,可以直接使用,一般通过描述Point 来进行动画的操作,可以有多个不同的状态变化。
接下来我们创建一个AnimationSummaryDemo 动画集合的 Demo,基础的 VC 使用 Storyboard , Demo 有简单移动、旋转、缩放、多点轨迹移动、曲线移动、组合动画、弹簧动画、转场动画,并且大部分动画,我们用一个简单的矩形作为直观的动画操作。
既然这么多个动画都有共同的操作 UI , 所以我们创建一个动画基类 WBBaseAnimationVC, 将 UI 放在一起, 在子类中,只展示动画的代码。
#import "ViewController.h" @protocol WBBaseAnimationDelegate- (void)starAnimation; - (void)removeAnimation; @end @interface WBBaseAnimationVC : UIViewController @property (nonatomic, strong) UIView *animationView; @property (nonatomic, readonly) UIButton *starAnimationButton; @property (nonatomic, readonly) UIButton *removeAnimationButton; @end
这里是贴上 头文件部分,具体看 Demo 。
创建一个WBSimpleMovingVC ,继承于WBBaseAnimationVC 。 .m 如下
#pragma mark - WBBaseAnimationDelegate methods - (void)starAnimation { //创建基础动画 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; //动画持续时间 animation.duration = 2.0f; //重复次数 animation.repeatCount = HUGE_VALF; //是否执行逆动画 animation.autoreverses = YES; //动画的速度变化 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; //动画的起始位置(当前) animation.fromValue = [NSValue valueWithCGPoint:self.animationView.layer.position]; //动画的终点位置 animation.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH - self.animationView.width / 2.0, SCREEN_HEIGHT - self.animationView.height / 2.0)]; [self.animationView.layer addAnimation:animation forKey:@"position"]; } - (void)removeAnimation { [self.animationView.layer removeAllAnimations]; }
创建WBRotatingVC ,继承于WBBaseAnimationVC , .m 如下
#import "WBRotatingVC.h" typedef NS_ENUM(NSUInteger, WBRotatingAxis) { WBRotatingAxisNone , WBRotatingAxisX , WBRotatingAxisY , WBRotatingAxisZ }; @interface WBRotatingVC () @end @implementation WBRotatingVC - (void)viewDidLoad { [super viewDidLoad]; UIButton *axisX = [UIButton buttonWithType:UIButtonTypeCustom]; axisX.titleLabel.font = [UIFont systemFontOfSize:15]; [axisX setTitle:@"X 轴旋转" forState:UIControlStateNormal]; [axisX setTitleColor:[UIColor colorWithRed:0.307 green:0.397 blue:1.000 alpha:1.000] forState:UIControlStateNormal]; [axisX setBackgroundColor:[UIColor colorWithWhite:0.814 alpha:1.000]]; [axisX addTarget:self action:@selector(rotatingWithAxisX:) forControlEvents:UIControlEventTouchUpInside]; UIButton *axisY = [UIButton buttonWithType:UIButtonTypeCustom]; axisY.titleLabel.font = [UIFont systemFontOfSize:15]; [axisY setTitle:@"Y 轴旋转" forState:UIControlStateNormal]; [axisY setTitleColor:[UIColor colorWithRed:0.307 green:0.397 blue:1.000 alpha:1.000] forState:UIControlStateNormal]; [axisY setBackgroundColor:[UIColor colorWithWhite:0.814 alpha:1.000]]; [axisY addTarget:self action:@selector(rotatingWithAxisY:) forControlEvents:UIControlEventTouchUpInside]; UIButton *axisZ = [UIButton buttonWithType:UIButtonTypeCustom]; axisZ.titleLabel.font = [UIFont systemFontOfSize:15]; [axisZ setTitle:@"Z 轴旋转" forState:UIControlStateNormal]; [axisZ setTitleColor:[UIColor colorWithRed:0.307 green:0.397 blue:1.000 alpha:1.000] forState:UIControlStateNormal]; [axisZ setBackgroundColor:[UIColor colorWithWhite:0.814 alpha:1.000]]; [axisZ addTarget:self action:@selector(rotatingWithAxisZ:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:axisX]; [self.view addSubview:axisY]; [self.view addSubview:axisZ]; [axisX mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(@0); make.top.equalTo(@129); make.width.equalTo(@80); make.height.equalTo(@40); }]; [axisY mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(@0); make.top.equalTo(axisX.mas_bottom).offset(10); make.width.equalTo(@80); make.height.equalTo(@40); }]; [axisZ mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(@0); make.top.equalTo(axisY.mas_bottom).offset(10); make.width.equalTo(@80); make.height.equalTo(@40); }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Private methods - (IBAction)rotatingWithAxisX:(id)sender { [self rotatingAnimationWithRotatingAxis:WBRotatingAxisX]; } - (IBAction)rotatingWithAxisY:(id)sender { [self rotatingAnimationWithRotatingAxis:WBRotatingAxisY]; } - (IBAction)rotatingWithAxisZ:(id)sender { [self rotatingAnimationWithRotatingAxis:WBRotatingAxisZ]; } - (void)rotatingAnimationWithRotatingAxis:(WBRotatingAxis)rotatingAxis { [self removeAnimation]; CABasicAnimation *animation; if (rotatingAxis == WBRotatingAxisNone) { animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; //平面沿着中心点旋转 } else if (rotatingAxis == WBRotatingAxisX) { animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"]; //沿 X } else if (rotatingAxis == WBRotatingAxisY) { animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; //沿 Y } else if (rotatingAxis == WBRotatingAxisZ) { animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; //沿 Z } //起始 animation.fromValue = [NSNumber numberWithFloat:0]; //旋转角度 animation.toValue = [NSNumber numberWithFloat:M_PI * 6]; //持续时间 animation.duration = 2.0f; //动画的速度变化 animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; //重复次数 animation.repeatCount = HUGE_VALF; [self.animationView.layer addAnimation:animation forKey:@"rotationAnimation"]; } #pragma mark - WBBaseAnimationDelegate methods - (void)starAnimation { [self rotatingAnimationWithRotatingAxis:WBRotatingAxisNone]; } - (void)removeAnimation { [self.animationView.layer removeAllAnimations]; } @end
创建WBZoomingVC , 继承于WBBaseAnimationVC , .m 如下
#pragma mark - WBBaseAnimationDelegate methods - (void)starAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; animation.duration = 1.5f; animation.repeatCount = HUGE_VALF; animation.autoreverses = YES; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; //起始倍率 animation.fromValue = [NSNumber numberWithFloat:1.0]; //结束时倍率 animation.toValue = [NSNumber numberWithFloat:2.0]; [self.animationView.layer addAnimation:animation forKey:@"scaleAnimation"]; } - (void)removeAnimation { [self.animationView.layer removeAllAnimations]; }
创建WBTrajectoryMovingVC ,继承于WBBaseAnimationVC , .m 如下
#pragma mark - WBBaseAnimationDelegate methods - (void)starAnimation { //创建BezierPath 对象 UIBezierPath *path = [UIBezierPath bezierPath]; //设定运行的起点 [path moveToPoint:self.animationView.layer.position]; //添加运动的轨迹直线点 [path addLineToPoint:CGPointMake(SCREEN_WIDTH - self.animationView.width / 2.0, SCREEN_HEIGHT / 2.0)]; [path addLineToPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT - self.animationView.width / 2.0)]; [path addLineToPoint:CGPointMake(self.animationView.width / 2.0, SCREEN_HEIGHT / 2.0)]; [path addLineToPoint:CGPointMake(SCREEN_WIDTH / 2.0, 64 + self.animationView.width / 2.0)]; [path closePath]; //创建关键帧动画 CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.path = path.CGPath; //将路径给予动画 pathAnimation.duration = 8.0; //持续时间 pathAnimation.repeatCount = HUGE_VALF; // 重复次数 // pathAnimation.autoreverses = YES; // 是否逆动画 [self.animationView.layer addAnimation:pathAnimation forKey:@"pathAnimation"]; } - (void)removeAnimation { [self.animationView.layer removeAllAnimations]; }
创建WBCurveMovingVC ,继承于WBBaseAnimationVC , .m 如下
#pragma mark - WBBaseAnimationDelegate methods - (void)starAnimation { //创建BezierPath 对象 UIBezierPath *path = [UIBezierPath bezierPath]; //设定运行的起点 [path moveToPoint:self.animationView.layer.position]; //添加轨迹点 // addQuadCurveToPoint 和 addCurveToPoint 都是曲线方法, 区别在于参数,addCurveToPoint 可以有两个基准点 controlPoint 作为划线的依据 [path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT * 0.75) controlPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT * 0.625)]; [path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT - self.animationView.height / 2.0) controlPoint:CGPointMake(0, SCREEN_HEIGHT * 0.875)]; [path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT * 0.75) controlPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT * 0.875)]; [path addQuadCurveToPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0) controlPoint:CGPointMake(0, SCREEN_HEIGHT * 0.625)]; // 关键帧动画 CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyFrameAnimation.path = path.CGPath; keyFrameAnimation.duration = 4.0f; keyFrameAnimation.repeatCount = HUGE_VALF; [self.animationView.layer addAnimation:keyFrameAnimation forKey:@"pathAnimation"]; } - (void)removeAnimation { [self.animationView.layer removeAllAnimations]; }
创建WBCombinationOneVC ,继承于WBBaseAnimationVC , .m 如下
#import "WBCombinationOneVC.h" @interface WBCombinationOneVC () @end @implementation WBCombinationOneVC - (void)viewDidLoad { [super viewDidLoad]; UIButton *animation1 = [UIButton buttonWithType:UIButtonTypeCustom]; animation1.titleLabel.font = [UIFont systemFontOfSize:15]; [animation1 setTitle:@"缩放+旋转" forState:UIControlStateNormal]; [animation1 setTitleColor:[UIColor colorWithRed:0.307 green:0.397 blue:1.000 alpha:1.000] forState:UIControlStateNormal]; [animation1 setBackgroundColor:[UIColor colorWithWhite:0.814 alpha:1.000]]; [animation1 addTarget:self action:@selector(combinationAnimationOne) forControlEvents:UIControlEventTouchUpInside]; UIButton *animation2 = [UIButton buttonWithType:UIButtonTypeCustom]; animation2.titleLabel.font = [UIFont systemFontOfSize:15]; [animation2 setTitle:@"轨+缩+Z旋" forState:UIControlStateNormal]; [animation2 setTitleColor:[UIColor colorWithRed:0.307 green:0.397 blue:1.000 alpha:1.000] forState:UIControlStateNormal]; [animation2 setBackgroundColor:[UIColor colorWithWhite:0.814 alpha:1.000]]; [animation2 addTarget:self action:@selector(animation2Click:) forControlEvents:UIControlEventTouchUpInside]; UIButton *animation3 = [UIButton buttonWithType:UIButtonTypeCustom]; animation3.titleLabel.font = [UIFont systemFontOfSize:15]; [animation3 setTitle:@"轨+缩+Y旋" forState:UIControlStateNormal]; [animation3 setTitleColor:[UIColor colorWithRed:0.307 green:0.397 blue:1.000 alpha:1.000] forState:UIControlStateNormal]; [animation3 setBackgroundColor:[UIColor colorWithWhite:0.814 alpha:1.000]]; [animation3 addTarget:self action:@selector(animation3Click:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:animation1]; [self.view addSubview:animation2]; [self.view addSubview:animation3]; [animation1 mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(@0); make.top.equalTo(@129); make.width.equalTo(@105); make.height.equalTo(@40); }]; [animation2 mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(@0); make.top.equalTo(animation1.mas_bottom).offset(10); make.width.equalTo(animation1.mas_width); make.height.equalTo(@40); }]; [animation3 mas_makeConstraints:^(MASConstraintMaker *make) { make.trailing.equalTo(@0); make.top.equalTo(animation2.mas_bottom).offset(10); make.width.equalTo(animation2.mas_width); make.height.equalTo(@40); }]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Private methods - (IBAction)animation2Click:(UIButton *)sender { [self combinationAnimationTwoWithAxis:@"Z"]; } - (IBAction)animation3Click:(UIButton *)sender { [self combinationAnimationTwoWithAxis:@"Y"]; } #pragma mark 旋转+缩放 - (void)combinationAnimationOne { //创建旋转动画 CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; rotateAnimation.fromValue = [NSNumber numberWithFloat:0]; rotateAnimation.toValue = [NSNumber numberWithFloat:M_PI * 8]; rotateAnimation.duration = 1.5f; rotateAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; rotateAnimation.repeatCount = HUGE_VALF; rotateAnimation.autoreverses = YES; //创建缩放动画 CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; scaleAnimation.duration = 1.5f; scaleAnimation.repeatCount = HUGE_VALF; scaleAnimation.autoreverses = YES; scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0]; scaleAnimation.toValue = [NSNumber numberWithFloat:3.0]; // 创建的动画组 CAAnimationGroup *groups = [CAAnimationGroup animation]; groups.animations = @[rotateAnimation, scaleAnimation]; groups.duration = 1.5f; groups.repeatCount = HUGE_VALF; groups.autoreverses = YES; [self.animationView.layer addAnimation:groups forKey:@"CombinationAnimation"]; } #pragma mark 移动+旋转+缩放 - (void)combinationAnimationTwoWithAxis:(NSString *)axis { //创建移动轨迹 UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:self.animationView.layer.position]; [path addLineToPoint:CGPointMake(SCREEN_WIDTH - self.animationView.width / 2.0, SCREEN_HEIGHT / 2.0)]; [path addLineToPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT - self.animationView.width / 2.0)]; [path addLineToPoint:CGPointMake(self.animationView.width / 2.0, SCREEN_HEIGHT / 2.0)]; [path addLineToPoint:CGPointMake(SCREEN_WIDTH / 2.0, 64 + self.animationView.width / 2.0)]; [path closePath]; //创建关键帧动画 CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.path = path.CGPath; //将路径给予动画 pathAnimation.duration = 8.0; //持续时间 pathAnimation.repeatCount = HUGE_VALF; // 重复次数 //创建缩放动画 CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; scaleAnimation.duration = 1.5f; scaleAnimation.repeatCount = HUGE_VALF; scaleAnimation.autoreverses = YES; scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; scaleAnimation.fromValue = [NSNumber numberWithFloat:1.0]; scaleAnimation.toValue = [NSNumber numberWithFloat:3.0]; //创建旋转动画 CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:[axis isEqualToString:@"Z"] ? @"transform.rotation" : @"transform.rotation.y"]; rotateAnimation.fromValue = [NSNumber numberWithFloat:0]; rotateAnimation.toValue = [NSNumber numberWithFloat:12]; rotateAnimation.duration = 0.5f; rotateAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; rotateAnimation.repeatCount = 4; rotateAnimation.autoreverses = YES; // 创建的动画组 CAAnimationGroup *groups = [CAAnimationGroup animation]; groups.animations = @[pathAnimation, rotateAnimation, scaleAnimation]; groups.duration = 8.0; groups.repeatCount = HUGE_VALF; groups.autoreverses = YES; [self.animationView.layer addAnimation:groups forKey:@"CombinationAnimation"]; } #pragma mark - WBBaseAnimationDelegate methods - (void)starAnimation { [self combinationAnimationTwoWithAxis:@"Z"]; } - (void)removeAnimation { [self.animationView.layer removeAllAnimations]; } @end
组合动画中,我们会有这样的思考,在创建 缩放动画 或者 旋转动画的时候,已经设置了 动画的持续时间 duration , 那么在创建动画组的时候,也设置了同样的属性,那么这样有什么不同呢? 还是依据时间最长的来呢?
其实我们可以这样想,比如设置了 旋转动画 的时间为 0.5f , 即是单位时间内执行一次动画,所需0.5f ,repeatCount 执行4次 ,那么动画组设置 8.0f, 那么意思就是,旋转的动画会执行 2.0f ,所以像图中, 矩形在运动到最左边的边缘时,就不在旋转了,只执行运动和缩放的动画了,即动画之间,还是可以分开管理的,可以设定在某一个时刻停止或继续执行某个动画 ,时间可以控制的,并不冲突。
创建WBSpringAnimationVC ,继承于WBBaseAnimationVC , .m 如下
#pragma mark - Private methods - (void)setupUI { self.animationView.hidden = YES; self.starAnimationButton.hidden = YES; self.removeAnimationButton.hidden = YES; self.basketballImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Basketball"]]; [self.view addSubview:self.basketballImageView]; [self.basketballImageView mas_makeConstraints:^(MASConstraintMaker *make) { make.centerX.equalTo(self.view.mas_centerX); make.centerY.equalTo(self.view.mas_centerY); make.width.height.equalTo(@50); }]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touch=touches.anyObject; CGPoint location= [touch locationInView:self.view]; /** * 弹性动画 * Duration 动画持续时间 * delay 动画延迟执行时间 * Damping 弹性阻尼,范围0.0~1.0 ,值越小,弹簧振幅越大 * Velocity 弹性复位的速度 * options 动画类型 * - returns: */ [UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{ self.basketballImageView.center = location; } completion:^(BOOL finished) { }]; }
创建WBTransitionsAnimationVC ,继承于WBBaseAnimationVC , .m 如下
#import "WBTransitionsAnimationVC.h" #import "WBTransitionsCell.h" @interface WBTransitionsAnimationVC ()@property (nonatomic, strong) UICollectionView *collectionView; @property (nonatomic, strong) NSArray<> *> *titles; @property (nonatomic, strong) NSArray *imagesNamed; @property (nonatomic, strong) UIImageView *imageView; @property (nonatomic, strong) UILabel *pageLabel; @property (nonatomic, assign) NSInteger currentIndex; //当前第几张图片 @property (nonatomic, copy) NSString *currentAnimationType; //当前动画类型 @end @implementation WBTransitionsAnimationVC - (void)viewDidLoad { [super viewDidLoad]; [self setupUI]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } #pragma mark - Private methods - (void)setupUI { self.animationView.hidden = YES; self.starAnimationButton.hidden = YES; self.removeAnimationButton.hidden = YES; self.titles = @[ @{@"fade" : @"淡出效果"} , @{@"movein" : @"新视图移动到旧视图上"} , @{@"push" : @"新视图推出旧视图"} , @{@"reveal" : @"移开旧视图显示新视图"} , @{@"cube" : @"立方体翻转效果"} , @{@"oglFlip" : @"翻转效果"} , @{@"suckEffect" : @"收缩效果"} , @{@"rippleEffect" : @"水滴波纹效果"} , @{@"pageCurl" : @"向上翻页效果"} , @{@"pageUnCurl" : @"向下翻页效果"} , @{@"cameralIrisHollowOpen" : @"摄像头打开效果"} , @{@"cameraIrisHollowClose" : @"摄像头关闭效果"} ]; self.imagesNamed = @[ @"picture1" , @"picture2" , @"picture3" , @"picture4" , @"picture5" , @"picture6" , @"picture7" , @"picture8" ]; UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; [layout setScrollDirection:UICollectionViewScrollDirectionHorizontal]; self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height) collectionViewLayout:layout]; [_collectionView registerClass:[WBTransitionsCell class] forCellWithReuseIdentifier:[WBTransitionsCell reuseIdentifier]]; _collectionView.backgroundColor = [UIColor clearColor]; _collectionView.alwaysBounceVertical = NO; _collectionView.showsHorizontalScrollIndicator = NO; _collectionView.delegate = self; _collectionView.dataSource = self; [self.view addSubview:self.collectionView]; self.imageView = [[UIImageView alloc] init]; _imageView.contentMode = UIViewContentModeScaleAspectFit; [self.view addSubview:self.imageView]; self.pageLabel = [[UILabel alloc] init]; self.pageLabel.textColor = [UIColor redColor]; self.pageLabel.textAlignment = NSTextAlignmentCenter; self.pageLabel.font = [UIFont systemFontOfSize:17]; [self.view addSubview:self.pageLabel]; [self.view setNeedsLayout]; [self.collectionView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(@70); make.leading.trailing.equalTo(@0); make.height.equalTo(@50); }]; [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.collectionView.mas_bottom).offset(40); make.leading.equalTo(@30); make.trailing.equalTo(@(-30)); make.bottom.equalTo(@(-40)); }]; [self.pageLabel mas_makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(self.imageView.mas_bottom); make.bottom.equalTo(@0); make.centerX.equalTo(self.view.mas_centerX); make.width.equalTo(@200); }]; [self.view layoutIfNeeded]; UISwipeGestureRecognizer *leftSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(leftSwipe:)]; leftSwipeGesture.direction = UISwipeGestureRecognizerDirectionLeft; [self.view addGestureRecognizer:leftSwipeGesture]; UISwipeGestureRecognizer *rightSwipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(rightSwipe:)]; rightSwipeGesture.direction = UISwipeGestureRecognizerDirectionRight; [self.view addGestureRecognizer:rightSwipeGesture]; [self setupDefaultValue]; } #pragma mark - Private methods - (void)setupDefaultValue { //默认图 self.currentIndex = 0; self.imageView.image = [self fetchCurrentImageWithIndex:self.currentIndex]; [self updatePageWithIndex:self.currentIndex]; //默认动画类型 NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; [self updateAnimationTypeWithIndexPath:indexPath]; } // 获取image - (UIImage *)fetchCurrentImageWithIndex:(NSInteger)index { return [UIImage imageNamed:self.imagesNamed[index]]; } // 更新动画类型 及 UI - (void)updateAnimationTypeWithIndexPath:(NSIndexPath *)indexPath { self.currentAnimationType = self.titles[indexPath.row].allKeys.firstObject; WBTransitionsCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:[WBTransitionsCell reuseIdentifier] forIndexPath:indexPath]; cell.selected = YES; } - (void)leftSwipe:(UISwipeGestureRecognizer *)gesture{ [self transitionAnimationDirection:YES]; } - (void)rightSwipe:(UISwipeGestureRecognizer *)gesture{ [self transitionAnimationDirection:NO]; } // 执行动画 - (void)transitionAnimationDirection:(BOOL)isNext { CATransition *transition = [[CATransition alloc] init]; //设置动画类型 transition.type = self.currentAnimationType; //设置动画时常 transition.duration = 1.0f; //设置方向 if (isNext) { transition.subtype = kCATransitionFromRight; self.currentIndex += 1; } else { transition.subtype = kCATransitionFromLeft; self.currentIndex -= 1; } if (self.currentIndex == -1) { self.currentIndex = self.imagesNamed.count - 1; } else if (self.currentIndex == self.imagesNamed.count) { self.currentIndex = 0; } [self updatePageWithIndex:self.currentIndex]; self.imageView.image = [self fetchCurrentImageWithIndex:self.currentIndex]; [self.imageView.layer addAnimation:transition forKey:@"transitionAnimation"]; } // 更新 Page - (void)updatePageWithIndex:(NSInteger)index { self.pageLabel.text = [NSString stringWithFormat:@"%ld / %ld", index + 1, self.imagesNamed.count]; } #pragma mark - UICollectionViewDataSource methods - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.titles.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { WBTransitionsCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[WBTransitionsCell reuseIdentifier] forIndexPath:indexPath]; [cell setupWithTitle:self.titles[indexPath.row].allValues.firstObject]; return cell; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { CGFloat minWidth = getTextWidth([UIFont systemFontOfSize:15], self.titles[indexPath.row].allValues.firstObject, 50).width; return CGSizeMake(minWidth + 20, collectionView.height); } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(0, 0, 0, 0); } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section{ return CGSizeMake(0, 0); } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 0; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 10; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { [self updateAnimationTypeWithIndexPath:indexPath]; } - (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath { WBTransitionsCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[WBTransitionsCell reuseIdentifier] forIndexPath:indexPath]; cell.selected = NO; } @end
转场动画中, 上面的选项 可以选择需要的转场动画类型。
将动画封装在 WBBaseAnimations中. .h 接口如下
#import@interface WBBaseAnimations : NSObject + (instancetype)sharedInstance; /** * 加入购物车 动画 * * @param view 需要动画的视图 * @param starRect 动画的起始位置 Rect (相对于window的位置) * @param finishPoint 动画的终点 Point * @param completed 动画完成回调 */ - (void)starAnimationWithView:(UIView *)view starRect:(CGRect)starRect finishPoint:(CGPoint)finishPoint completedBlock:(void (^)(BOOL finish))completed; /** * 摇一摇动画 * * @param view 需要动画的视图 * @param completed 动画完成回调 */ - (void)shakeAnimationWithView:(UIView *)view completedBlock:(void (^)(BOOL finish))completed; @end
在WBShoppingCartVC 中使用购物车动画, .m 如下
#import "WBShoppingCartVC.h" #import "WBShoppingCartCell.h" #import "WBBaseAnimations.h" @interface WBShoppingCartVC () @property (strong, nonatomic) IBOutlet UITableView *tableView; @end @implementation WBShoppingCartVC - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Private methods - (void)addCartAnimationWithView:(UIView *)view { UIWindow *window = [[UIApplication sharedApplication].delegate window]; CGRect goodsImageRect = [view convertRect:view.bounds toView:window]; //动画期间禁止交互,可以一次只执行一次动画. // window.userInteractionEnabled = NO; WBBaseAnimations *animation = [WBBaseAnimations sharedInstance]; [animation starAnimationWithView:view starRect:goodsImageRect finishPoint:CGPointMake(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT - 49) completedBlock:^(BOOL finish) { if (finish) { UIView *tabbarView = self.tabBarController.tabBar.subviews[2]; [animation shakeAnimationWithView:tabbarView completedBlock:^(BOOL finish) { if (finish) { // window.userInteractionEnabled = YES; } }]; } }]; } #pragma mark - UITableViewDelegate methods - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 10; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { WBShoppingCartCell *cell = [tableView dequeueReusableCellWithIdentifier:@"shoppingCartCell" forIndexPath:indexPath]; [cell setAddCartGoodsImageViewBlock:^(UIImageView *imageView) { [self addCartAnimationWithView:imageView]; }]; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @end