干貨系列之實(shí)現(xiàn)City Guides的動(dòng)畫效果(一)

點(diǎn)此下載源碼下載:源碼(會(huì)持續(xù)更新,歡迎star。保證炫酷,童叟無欺?。?/p>

City Guides App.jpg

dribbble的設(shè)計(jì)師的作品中了解到City Guides上非常優(yōu)秀的動(dòng)畫效果。感嘆設(shè)計(jì)師很棒的設(shè)計(jì)的同時(shí),小編也在心里默想這些動(dòng)畫是怎么實(shí)現(xiàn)的。于是從App Store上下載了APP,使用然后仔細(xì)研究后便有了此篇文章。

City Guides中幾乎所有的界面展示與交互方式都是有動(dòng)畫效果的。小編也就分幾部分來實(shí)現(xiàn)動(dòng)畫效果。

這一篇要實(shí)現(xiàn)的動(dòng)畫效果如下:

第一個(gè)動(dòng)畫效果:

ZFCityGuides-One.gif

第二個(gè)動(dòng)畫效果:

ZFCityGuides-two.gif

本篇文章只講解實(shí)現(xiàn)思路,具體的可以參見源碼。

動(dòng)畫效果一

第一個(gè)動(dòng)畫效果,實(shí)際上包括三個(gè)部分動(dòng)畫效果。選中動(dòng)畫效果、動(dòng)畫轉(zhuǎn)場(chǎng)和切換動(dòng)畫效果。本篇文章中所有的動(dòng)畫,使用POP和Core Animation實(shí)現(xiàn)。

選中動(dòng)畫效果

此動(dòng)畫實(shí)現(xiàn)相對(duì)簡(jiǎn)單一些,中間部分是使用的UICollectionView,創(chuàng)建了四個(gè)cell。在可視的cell中實(shí)現(xiàn)被選中的cell放大,其余可視的cell縮放的同時(shí)透明度減少。使用POP實(shí)現(xiàn)的方法如下:

- (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];
            
        }
    }

}

此部分實(shí)現(xiàn)的效果圖:

ZFCityGuides-1.gif

動(dòng)畫轉(zhuǎn)場(chǎng)

此部分的轉(zhuǎn)場(chǎng)過渡動(dòng)畫效果,參考小編上篇文章講解的自定義轉(zhuǎn)場(chǎng)。里面涉及到手勢(shì)交互,下一篇文章再做詳細(xì)講解。

切換效果動(dòng)畫

實(shí)際上是點(diǎn)擊最上面的兩個(gè)button后,對(duì)應(yīng)的視圖動(dòng)畫效果。

切換到SLIDES后,UICollectionView整個(gè)視圖向左移,移出當(dāng)前屏幕。與此同時(shí)可視的UICollectionViewCell隨著變化,而且每個(gè)cell的變化有區(qū)別。區(qū)別是:移出的時(shí)候,第二個(gè)cell最后消失在視線里(偏移量最?。谒膫€(gè)cell旋轉(zhuǎn)幅度較大(角度最大)。
實(shí)現(xiàn)的方式:

-(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 移動(dòng)
    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整個(gè)視圖向右移,漸入到當(dāng)前屏幕。UICollectionViewCell變化與上面有些不一樣。第3個(gè)cell 和第4個(gè)cell最后完成動(dòng)畫,其動(dòng)畫變化較大。

-(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 移動(dòng)
    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];
        }

    }];

}

以上兩個(gè)動(dòng)畫完成的過程中,仔細(xì)觀察SLIDES下對(duì)應(yīng)的視圖,UIScrollView有一些細(xì)微的變化。在Y方向上有一定的偏移量的變化。是根據(jù)UICollectionView移出的位置變化而改變。使用POPAnimationDelegate的方法,根據(jù)當(dāng)前執(zhí)行動(dòng)畫決定在Y方向偏移量是逐步增加或減少。

- (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];
    }
}

具體效果參見下圖:

ZFCityGuides-2.gif

動(dòng)畫效果二

此部分動(dòng)畫是使用UIScrollView來實(shí)現(xiàn),同樣可以使用UICollectionView來實(shí)現(xiàn)。此處是UICollectionView實(shí)現(xiàn)。本篇文章使用繼承UIScrollView的類來實(shí)現(xiàn)。只需要以下幾行代碼即可。是根據(jù)UIScrollView的子視圖偏移量的變化實(shí)現(xiàn)。參考以下代碼


#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;
        //計(jì)算每個(gè)cell的偏移量
        CGFloat distanceFromCenterX = view.frame.origin.x - contentOffsetX;
  
        CGFloat offsetRatio = distanceFromCenterX / CGRectGetWidth(self.frame);
        CGFloat angle = offsetRatio * 30;
        //前半部分
        if (offsetRatio < 0) {
            //offsetRation 為負(fù)
            //沿y軸向右轉(zhuǎn) 沿著y的正方向看是逆時(shí)針
            CATransform3D t2 = CATransform3DMakeRotation(DEGREES_TO_RADIANS(-angle), 0, 1,0 );
            //沿x軸向里轉(zhuǎn) 沿著x的正方向看是逆時(shí)針
            CATransform3D t3 = CATransform3DRotate(t2, DEGREES_TO_RADIANS(-5 * offsetRatio) , 1, 0, 0);
            //沿y軸向下移動(dòng) 距離正
            CATransform3D t4 = CATransform3DTranslate(t3, 0, -35 *offsetRatio, 0);
            //實(shí)現(xiàn)透視投影 默認(rèn)是正交投影 以CGPointMake(0, 0)為觀察點(diǎn) 3D效果更明顯
            view.layer.transform = CATransform3DPerspect(t4, CGPointMake(0, 0), 500);
        }
        else{
            //給個(gè)起始位置 在正常位置以下以左的某段距離
            t1 = CATransform3DMakeTranslation(60 * offsetRatio * 1.5, 100 * offsetRatio * 1.5, 0);
            //沿y軸向左轉(zhuǎn) 沿著y的正方向看是順時(shí)針
            CATransform3D t2 = CATransform3DRotate(t1,DEGREES_TO_RADIANS(-angle), 0, 1,0 );
            //沿x軸向里轉(zhuǎn) 沿著x的正方向看是逆時(shí)針
            CATransform3D t3 = CATransform3DRotate(t2, DEGREES_TO_RADIANS(5 * offsetRatio), 1, 0, 0);
            //沿y軸向上移動(dòng) 距離正
            CATransform3D t4 = CATransform3DTranslate(t3, 0, -35 *offsetRatio, 0);
            //實(shí)現(xiàn)透視投影 默認(rèn)是正交投影 以CGPointMake(0, 0)為觀察點(diǎn) 3D效果更明顯
            view.layer.transform = CATransform3DPerspect(t4, CGPointMake(0, 0), 500);
        }

    }
}

@end

其中CATransform3DPerspect實(shí)現(xiàn)方法和使用原理可以參考此篇文章iOS 3D UI---CALayer的transform擴(kuò)展

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));
}

后續(xù)實(shí)現(xiàn)內(nèi)容,請(qǐng)持續(xù)關(guān)注小編。

ZFCityGuides3.gif

點(diǎn)此下載源碼下載:源碼(會(huì)持續(xù)更新,歡迎star)

擴(kuò)展閱讀

干貨系列之實(shí)現(xiàn)City Guides的動(dòng)畫效果(二)

iOS 3D UI---CALayer的transform擴(kuò)展

干貨系列之實(shí)戰(zhàn)詳解自定義轉(zhuǎn)場(chǎng)動(dòng)畫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,062評(píng)論 4 61
  • 嗯哼嗯哼蹦擦擦~~~ 轉(zhuǎn)載自:https://github.com/Tim9Liu9/TimLiu-iOS 目錄 ...
    philiha閱讀 5,235評(píng)論 0 6
  • 我是一個(gè)90后妹子,在大學(xué)畢業(yè)了以后最開心的事情就是可以拒絕書本,可以拒絕一切自己不喜歡的事。 但同時(shí),我又是個(gè)“...
    我是婷玉呀閱讀 718評(píng)論 8 11
  • 身邊的人幾乎都在玩微信,刷朋友圈,但卻不是人人都在朋友圈發(fā)信息。 有些人只是默默關(guān)注別人的狀態(tài),但卻從來不發(fā)朋友圈...
    莞美無瑕閱讀 694評(píng)論 0 1
  • “一年之際在于春” 趁著美好時(shí)光出去轉(zhuǎn)轉(zhuǎn),走著… 看吧公園再去瞧瞧牡丹… 美吧!在看就是下一年了!時(shí)光真的如梭??!...
    天山一花閱讀 213評(píng)論 0 0

友情鏈接更多精彩內(nèi)容