點此下載原始碼下載:原始碼(會持續更新,歡迎star。保證炫酷,童叟無欺!)
從dribbble的設計師的作品中瞭解到City Guides上非常優秀的動畫效果。感嘆設計師很棒的設計的同時,小編也在心裡默想這些動畫是怎麼實現的。於是從App Store上下載了APP,使用然後仔細研究後便有了此篇文章。
City Guides中幾乎所有的介面展示與互動方式都是有動畫效果的。小編也就分幾部分來實現動畫效果。
這一篇要實現的動畫效果如下:
第一個動畫效果:
第二個動畫效果:
本篇文章只講解實現思路,具體的可以參見原始碼。
動畫效果一
第一個動畫效果,實際上包括三個部分動畫效果。選中動畫效果、動畫轉場和切換動畫效果。本篇文章中所有的動畫,使用POP和Core Animation實現。
選中動畫效果
此動畫實現相對簡單一些,中間部分是使用的UICollectionView,建立了四個cell。在可視的cell中實現被選中的cell放大,其餘可視的cell縮放的同時透明度減少。使用POP實現的方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewCell *selectedCell = (UICollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath]; for (UICollectionViewCell *cell in collectionView.visibleCells) { if ([cell isEqual:selectedCell]) { //選中的cell的放大 POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; scaleAnimation.duration = 0.5; scaleAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(1.05, 1.05)]; [cell.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"]; POPBasicAnimation *alphaAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha]; alphaAnimation.duration = 0.5; alphaAnimation.toValue = @1.0; [cell pop_addAnimation:alphaAnimation forKey:nil]; [alphaAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { // if (finished) { // // ZFMainTabBarController *mainTabBarVC = [[ZFMainTabBarController alloc] init]; // mainTabBarVC.modalPresentationStyle = UIModalPresentationOverCurrentContext; // mainTabBarVC.transitioningDelegate = self; // // self.interactionViewController = [ZFInteractiveTransition new]; // [self presentViewController:mainTabBarVC animated:YES completion:NULL]; // } }]; } else{ //未選中的可視cell的縮放 POPBasicAnimation *scaleAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPLayerScaleXY]; scaleAnimation.duration = 0.5; scaleAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(0.95, 0.95)]; [cell.layer pop_addAnimation:scaleAnimation forKey:@"scaleAnimation"]; POPBasicAnimation *alphaAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewAlpha]; alphaAnimation.duration = 0.5; alphaAnimation.toValue = @0.7; [cell pop_addAnimation:alphaAnimation forKey:nil]; } } } |
此部分實現的效果圖:
動畫轉場
此部分的轉場過渡動畫效果,參考小編上篇文章講解的自定義轉場。裡面涉及到手勢互動,下一篇文章再做詳細講解。
切換效果動畫
實際上是點選最上面的兩個button後,對應的檢視動畫效果。
切換到SLIDES後,UICollectionView整個檢視向左移,移出當前螢幕。與此同時可視的UICollectionViewCell隨著變化,而且每個cell的變化有區別。區別是:移出的時候,第二個cell最後消失在視線裡(偏移量最小),第四個cell旋轉幅度較大(角度最大)。
實現的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
-(void)showSlideLayout:(UIButton *)sender{ if (_SliderButton.selected) { return; } [_scrollView setHidden:NO]; _SliderButton.selected = YES; _GridButton.selected = NO; _SliderButton.userInteractionEnabled = NO; [self canEnableClick]; _backgroundLayer.frame = sender.frame; int i = 0; for (UICollectionViewCell *cell in _cityCollectView.visibleCells) { NSArray *translationArray = @[@-300, @-200, @-600, @-600]; NSArray *angles = @[@(-15* M_PI/180), @(-30* M_PI/180), @(-15* M_PI/180), @(-60* M_PI/180)]; NSArray *scaleArray = @[@0, @0, @0, @-5]; //未選中的可視cell的縮放 POPBasicAnimation *rotationAnimation = [POPBasicAnimation easeInEaseOutAnimation]; rotationAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerRotation]; rotationAnimation.duration = duration; rotationAnimation.fromValue = @(0); rotationAnimation.toValue = angles[i]; POPBasicAnimation *translationAnimation = [POPBasicAnimation easeInEaseOutAnimation]; translationAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerTranslationX]; translationAnimation.duration = duration; translationAnimation.fromValue = @(0); translationAnimation.toValue = translationArray[i]; POPBasicAnimation *zPositionAnimation = [POPBasicAnimation easeInEaseOutAnimation]; zPositionAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerZPosition]; zPositionAnimation.duration = duration; zPositionAnimation.toValue = scaleArray[i]; [cell.layer pop_addAnimation:rotationAnimation forKey:@"rotationAnimation"]; [cell.layer pop_addAnimation:translationAnimation forKey:@"translationAnimation"]; [cell.layer pop_addAnimation:zPositionAnimation forKey:@"zPositionAnimation"]; i++; } // collectionView 移動 CGRect fromFrame = _cityCollectView.frame; CGRect toFrome = fromFrame; fromFrame.origin.x -= fromFrame.size.width; _cityCollectView.frame = fromFrame; POPBasicAnimation *frameAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewFrame]; frameAnimation.name = @"showSliderView"; frameAnimation.duration = duration; frameAnimation.fromValue = [NSValue valueWithCGRect:toFrome]; frameAnimation.toValue = [NSValue valueWithCGRect:fromFrame]; frameAnimation.delegate = self; [_cityCollectView pop_addAnimation:frameAnimation forKey:@"frameAnimation"]; [frameAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { if (finished) { [_cityCollectView setHidden:YES]; } }]; } |
切換到GRID後,UICollectionView整個檢視向右移,漸入到當前螢幕。UICollectionViewCell變化與上面有些不一樣。第3個cell 和第4個cell最後完成動畫,其動畫變化較大。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
-(void)showGridLayout:(UIButton *)sender{ if (_GridButton.selected) { return; } [_cityCollectView setHidden:NO]; _backgroundLayer.frame = sender.frame; _SliderButton.selected = NO; _GridButton.selected = YES; _GridButton.userInteractionEnabled = NO; [self canEnableClick]; int i = 0; for (UICollectionViewCell *cell in _cityCollectView.visibleCells) { NSArray *translationArray = @[@-5, @0, @-300, @-100]; NSArray *angles = @[@(-2* M_PI/180), @(-5* M_PI/180), @(-30* M_PI/180), @(-10* M_PI/180)]; //未選中的可視cell的縮放 POPBasicAnimation *rotationAnimation = [POPBasicAnimation easeInEaseOutAnimation]; rotationAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerRotation]; rotationAnimation.duration = duration; rotationAnimation.fromValue = angles[i]; rotationAnimation.toValue = @(0); POPBasicAnimation *translationAnimation = [POPBasicAnimation easeInEaseOutAnimation]; translationAnimation.property = [POPAnimatableProperty propertyWithName:kPOPLayerTranslationX]; translationAnimation.duration = duration; translationAnimation.fromValue = translationArray[i]; translationAnimation.toValue = @(0); [cell.layer pop_addAnimation:rotationAnimation forKey:@"rotationAnimation"]; [cell.layer pop_addAnimation:translationAnimation forKey:@"translationAnimation"]; i++; } // collectionView 移動 CGRect fromFrame = _cityCollectView.frame; CGRect toFrome = fromFrame; toFrome.origin.x = 0; POPBasicAnimation *frameAnimation = [POPBasicAnimation animationWithPropertyNamed:kPOPViewFrame]; frameAnimation.name = @"showGridView"; frameAnimation.duration = duration; frameAnimation.fromValue = [NSValue valueWithCGRect:fromFrame]; frameAnimation.toValue = [NSValue valueWithCGRect:toFrome]; frameAnimation.delegate = self; [_cityCollectView pop_addAnimation:frameAnimation forKey:@"frameAnimation"]; [frameAnimation setCompletionBlock:^(POPAnimation *anim, BOOL finished) { if (finished) { [_scrollView setHidden:YES]; } }]; } |
以上兩個動畫完成的過程中,仔細觀察SLIDES下對應的檢視,UIScrollView有一些細微的變化。在Y方向上有一定的偏移量的變化。是根據UICollectionView移出的位置變化而改變。使用POPAnimationDelegate的方法,根據當前執行動畫決定在Y方向偏移量是逐步增加或減少。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
- (void)pop_animationDidApply:(POPAnimation *)anim { // NSLog(@"%@",anim.name ); CGRect currentValue = [[anim valueForKey:@"currentValue"] CGRectValue]; if ([anim.name isEqualToString:@"showSliderView"]) { CGRect frame = _scrollView.frame; CGFloat distanceY = 30 *(1 -fabs(currentValue.origin.x /_scrollView.frame.size.width)); frame.origin.x = _scrollView.frame.size.width + currentValue.origin.x; frame.origin.y = CGRectGetMaxY(_SliderButton.frame) + 20. + distanceY ; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.0]; _scrollView.frame = frame; [UIView commitAnimations]; } else if([anim.name isEqualToString:@"showGridView"]){ CGRect frame = _scrollView.frame; CGFloat distanceY = 30 * (1 - fabs(currentValue.origin.x /_scrollView.frame.size.width)); frame.origin.x = _scrollView.frame.size.width + currentValue.origin.x; frame.origin.y = CGRectGetMaxY(_SliderButton.frame) + 20. + distanceY ; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.0]; _scrollView.frame = frame; [UIView commitAnimations]; } } |
具體效果參見下圖:
動畫效果二
此部分動畫是使用UIScrollView來實現,同樣可以使用UICollectionView來實現。此處是UICollectionView實現。本篇文章使用繼承UIScrollView的類來實現。只需要以下幾行程式碼即可。是根據UIScrollView的子檢視偏移量的變化實現。參考以下程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#import "ZFTransformScrollView.h" @implementation ZFTransformScrollView -(void)layoutSubviews{ [super layoutSubviews]; CGFloat contentOffsetX = self.contentOffset.x; for(UIView *view in self.subviews){ CATransform3D t1 = CATransform3DIdentity; view.layer.transform = t1; //計算每個cell的偏移量 CGFloat distanceFromCenterX = view.frame.origin.x - contentOffsetX; CGFloat offsetRatio = distanceFromCenterX / CGRectGetWidth(self.frame); CGFloat angle = offsetRatio * 30; //前半部分 if (offsetRatio < 0) { //offsetRation 為負 //沿y軸向右轉 沿著y的正方向看是逆時針 CATransform3D t2 = CATransform3DMakeRotation(DEGREES_TO_RADIANS(-angle), 0, 1,0 ); //沿x軸向裡轉 沿著x的正方向看是逆時針 CATransform3D t3 = CATransform3DRotate(t2, DEGREES_TO_RADIANS(-5 * offsetRatio) , 1, 0, 0); //沿y軸向下移動 距離正 CATransform3D t4 = CATransform3DTranslate(t3, 0, -35 *offsetRatio, 0); //實現透視投影 預設是正交投影 以CGPointMake(0, 0)為觀察點 3D效果更明顯 view.layer.transform = CATransform3DPerspect(t4, CGPointMake(0, 0), 500); } else{ //給個起始位置 在正常位置以下以左的某段距離 t1 = CATransform3DMakeTranslation(60 * offsetRatio * 1.5, 100 * offsetRatio * 1.5, 0); //沿y軸向左轉 沿著y的正方向看是順時針 CATransform3D t2 = CATransform3DRotate(t1,DEGREES_TO_RADIANS(-angle), 0, 1,0 ); //沿x軸向裡轉 沿著x的正方向看是逆時針 CATransform3D t3 = CATransform3DRotate(t2, DEGREES_TO_RADIANS(5 * offsetRatio), 1, 0, 0); //沿y軸向上移動 距離正 CATransform3D t4 = CATransform3DTranslate(t3, 0, -35 *offsetRatio, 0); //實現透視投影 預設是正交投影 以CGPointMake(0, 0)為觀察點 3D效果更明顯 view.layer.transform = CATransform3DPerspect(t4, CGPointMake(0, 0), 500); } } } @end |
其中CATransform3DPerspect實現方法和使用原理可以參考此篇文章iOS 3D UI—CALayer的transform擴充套件
1 2 3 4 5 6 7 8 9 10 11 12 13 |
CATransform3D CATransform3DMakePerspective(CGPoint center, float disZ) { CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0); CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 0); CATransform3D scale = CATransform3DIdentity; scale.m34 = -1.0f/disZ; return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack); } CATransform3D CATransform3DPerspect(CATransform3D t, CGPoint center, float disZ) { return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ)); } |
後續實現內容,請持續關注小編。
點此下載原始碼下載:原始碼(會持續更新,歡迎star)