最近剛好需要用到一些動畫效果,所以對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
