折疊效果|動(dòng)畫

前言

為什么我們要做這個(gè)效果裝逼炫技??沒有別的了,廢話少說先看看效果


折疊.gif

下方的箭頭主要是來確定,整個(gè)控件的高度,是隨著折疊而變化的。

分析

1、首先遇到問題還是先分析。整個(gè)控件是一個(gè)折疊容器,然后容器里有很多個(gè)小的可以折疊的控件,小的折疊控件,折疊的時(shí)候我們把錨點(diǎn)移到y(tǒng)=0,然后在旋轉(zhuǎn)180°

錨點(diǎn)位置

2、在每一個(gè)可旋轉(zhuǎn)的控件我們首先要做的是截圖,把整個(gè)一個(gè)可能很復(fù)雜的控件變?yōu)橐粡?code>imageview這樣方便我們待會(huì)做旋轉(zhuǎn)。
3、要做出“翻過來”的效果我這里是使用一個(gè)模糊效果的imageview加載上面,旋轉(zhuǎn)180°的過程中并不是一次性完成的,因?yàn)槲覀內(nèi)绻峭险郫B,旋轉(zhuǎn)到90°的時(shí)候我們應(yīng)該是看到控件的背部,所以我們在旋轉(zhuǎn)90°動(dòng)畫完成的后,要把“背部”放到最前面,看上去就像翻過去了。
大致的分析就到這兒,其中還有很多細(xì)節(jié)需要處理

準(zhǔn)備

  • 第一步我們要配置好折疊容器中的可折疊元素,(這個(gè)demo中的0、1、2、3、4)
/**
 配置折疊元素
 */
- (void)configurationFoldItem {
    self.unfoldArrayM           = [NSMutableArray arrayWithCapacity:self.itemCount];
    for (int  i = 0; i < self.itemCount; i ++ ) {
        CGRect rect             = CGRectMake(0 , i * self.itemHeight, self.itemWidth, self.itemHeight);
        UIImage *image          = [self ai_takeSnapshotWithFrame:rect];
        AIFoldRotatedView *rotatedView      = [[AIFoldRotatedView alloc]initWithFrame:rect Image:image];
//        rotatedView.layer.anchorPoint    = CGPointMake(.5, 0);
//        //測試
//        rotatedView.tag         = i+100;
        rotatedView.delegate    = self;
        [self addSubview:rotatedView];
        //添加到可折疊數(shù)組中
        [self.itemArrayM addObject:rotatedView];
        //保存到展開數(shù)組中
        [self.unfoldArrayM addObject:[NSValue valueWithCGRect:rect]];
    }
    self.contentView.alpha                  = 0.;
}

實(shí)現(xiàn)

在自定義一個(gè)控件的時(shí)候我們不要立馬去實(shí)現(xiàn)某一個(gè)方法,而是先想一下我們需要什么屬性,需要什么方法,每一個(gè)方法需要哪些對應(yīng)的參數(shù)。我覺得這才是一個(gè)面向?qū)ο缶幊痰乃枷搿?br> 我們需要讓旋轉(zhuǎn)控件做展開或者折疊的效果,所以我們要有展開和折疊的方法,展開是要時(shí)間的,所以參數(shù)有duration,我們的折疊容器中可折疊的控件不止一個(gè)要做到一個(gè)控件折疊完成后再折疊第二個(gè)所以我們有參數(shù)delay

/**
 旋轉(zhuǎn)180度
 
 @param duration 持續(xù)時(shí)長
 @param delay 延時(shí)
 */
- (void)foldingAnimationMI_PWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay;
/**
 展開旋轉(zhuǎn)180度
 
 @param duration 持續(xù)時(shí)長
 @param delay 延時(shí)
 */
- (void)unfoldingAnimationMI_PWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay;

在將要折疊和折疊完成后,我們需要做一些事情,比如修改折疊容器的高度或者說修改狀態(tài),我這里是使用協(xié)議代理實(shí)現(xiàn)的

@protocol AIFoldRotatedViewDelegate<NSObject>

/**
 折疊完成后回調(diào)

 @param roatatedView 折疊控件
 @param anim anim description
 @param flag flag description
 */
- (void)foldRotatedView:(AIFoldRotatedView*)roatatedView animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
/**
 展開完成后回調(diào)
 
 @param roatatedView 折疊控件
 @param anim anim description
 @param flag flag description
 */
- (void)unfoldRotatedView:(AIFoldRotatedView*)roatatedView animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

/**
 將要折疊回調(diào)
 
 @param roatatedView 折疊控件
 
 */
- (void)willfoldRotatedView:(AIFoldRotatedView*)roatatedView;
/**
 將要展開回調(diào)

 @param roatatedView 折疊控件

 */
- (void)willUnfoldRotatedView:(AIFoldRotatedView*)roatatedView;


@end

在我們剛才大致的分析了之后,現(xiàn)在我們看如何實(shí)現(xiàn)這些方法
其中最最核心的就是我們要把這個(gè)控件旋轉(zhuǎn)一個(gè)角度,不管是展開還是折疊都是旋轉(zhuǎn)動(dòng)畫所以我旋轉(zhuǎn)動(dòng)畫封裝成一個(gè)方法

/**
 旋轉(zhuǎn)動(dòng)畫
 
 @param timing 節(jié)奏
 @param from 開始
 @param to 結(jié)束
 @param duration 持續(xù)時(shí)長
 @param delay 延時(shí)
 */
- (CABasicAnimation*)rotationAnimationTiming:(NSString *)timing from:(CGFloat)from to:(CGFloat)to duration:(NSTimeInterval)duration delay:(NSTimeInterval)delay  {
    CABasicAnimation *rotateAnimation     = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
    rotateAnimation.timingFunction        = [CAMediaTimingFunction functionWithName:timing];
    rotateAnimation.fromValue             = @(from);
    rotateAnimation.toValue               = @(to);
    rotateAnimation.duration              = duration;
    rotateAnimation.delegate              = self;
    rotateAnimation.fillMode              = kCAFillModeForwards;
    rotateAnimation.removedOnCompletion   = NO;
    rotateAnimation.beginTime             = CACurrentMediaTime() + delay;
    return rotateAnimation;
}

接下來我以折疊為例,展開的方法和折疊類似


/**
 折疊旋轉(zhuǎn)180度

 @param duration 持續(xù)時(shí)長
 @param delay 延時(shí)
 */
- (void)foldingAnimationMI_PWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay {

    [self clearTransform];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.layer.position       = CGPointMake(CGRectGetMidX(self.frame), self.ai_y );
        self.layer.anchorPoint    = CGPointMake(.5, 0);
        if (self.delegate && [self.delegate respondsToSelector:@selector(willfoldRotatedView:)]) {
            [self.delegate willfoldRotatedView:self ];
        }else {
            AILog(@"未設(shè)置代理");
        }
    });
    CABasicAnimation *animation1Layer          = [self rotationAnimationTiming:kCAMediaTimingFunctionEaseIn from:0 to:M_PI_2 duration:duration * .5 delay:delay ];
    [animation1Layer setValue:@"foldstarAnimation" forKey:@"name"];
    [self.layer addAnimation:animation1Layer forKey:@"animation1"];
}

1、大家可以看到我這里并沒有旋轉(zhuǎn)180°,而是旋轉(zhuǎn)的90°這是因?yàn)槲以谇懊嬷v的我們要實(shí)現(xiàn)折疊到一半,我們應(yīng)該看到的是控件的"背面",這里我動(dòng)畫拿到了添加了key/value就是為了確定動(dòng)畫結(jié)束的地方(這個(gè)技巧我在下載按鈕|動(dòng)畫中也有使用)

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    NSString *name = [anim valueForKey:@"name"];
    if ([name isEqualToString:@"foldstarAnimation"]) {//折疊到90°
        // 讓backView到最前面來
        [self bringSubviewToFront:self.backView];
        CABasicAnimation *foldendAnimation          = [self rotationAnimationTiming:kCAMediaTimingFunctionEaseOut from:M_PI_2 to:M_PI duration:anim.duration delay:0 ];
        [foldendAnimation setValue:@"foldendAnimation" forKey:@"name"];
        [self.layer addAnimation:foldendAnimation forKey:nil];
        
    }else if([name isEqualToString:@"foldendAnimation"]){ //折疊完成
//        [self.layer removeAllAnimations];
        [self rotatedXWithAngle:0];
        [self clearTransform];
        if (self.delegate && [self.delegate respondsToSelector:@selector(foldRotatedView:animationDidStop:finished:)]) {
            [self.delegate foldRotatedView:self animationDidStop:anim finished:flag];
        }else {
            AILog(@"未設(shè)置代理");
        }
    }
}

當(dāng)折疊到90°的時(shí)候我們把背景圖,放到前面來,形成一個(gè)"看到控件背后的效果",整個(gè)180°旋轉(zhuǎn)完成后調(diào)用折疊完成的回調(diào)函數(shù),這里有點(diǎn)要注意,我們要清理layertransform因?yàn)槲覀兪怯卸鄠€(gè)可旋轉(zhuǎn)的控件,還原transform在下一個(gè)控件旋轉(zhuǎn)的時(shí)候錨點(diǎn)就可能出問題,這個(gè)特別是在展開的時(shí)候體現(xiàn)非常明顯
2、一個(gè)控件折疊完成后,需要修改的他的frame和把已經(jīng)折疊的這個(gè)控件放在,讓已經(jīng)折疊這個(gè)控件成為上一個(gè)控件的子控件
最后折疊完成后層級關(guān)系應(yīng)該是這樣

折疊完成后層級關(guān)系

/**
 一個(gè)疊完成后回調(diào)

 @param roatatedView 折疊視圖
 @param anim anim description
 @param flag flag description
 */
-(void)foldRotatedView:(AIFoldRotatedView *)roatatedView animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {

    [roatatedView.layer removeAllAnimations];
    NSInteger index    = [self.itemArrayM indexOfObject:roatatedView];
    self.descView      = self.itemArrayM[index-1];
    roatatedView.frame = self.descView.bounds;
    [self.descView addSubview:roatatedView];

}

GitHub上查看源碼,你的star是我最大的支持

源碼位置

參考文獻(xiàn)

參考開源庫:popping
folding-cell
參考博客:http://blog.sina.com.cn/s/blog_8f5097be0101b91z.html

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

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評論 25 709
  • 《裕語言》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 28,703評論 5 20
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,046評論 4 61
  • 為什么要不開心? 我無法自圓其說 這開放性的話題 你的甜言讓筆尖生了銹 斷了魂 不到情非得已 我只是情非得已 為什...
    倩何人換取閱讀 370評論 0 2
  • 阿禾是典型的雙魚座女生,低調(diào)內(nèi)斂,多愁善感,喜歡胡思亂想。 在遇見褚柒以前,她總以為自己會(huì)沒人愛地孤單一輩子。 遇...
    吳困困閱讀 953評論 1 10

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