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