App Store中Today轉(zhuǎn)場動畫實現(xiàn)

先看動畫薛夜,這是仿后的效果

Untitled.gif

這轉(zhuǎn)場動畫,乍看一下很復(fù)雜肮塞,其實是拆化細分之后是很簡單的襟齿。大部分動畫都只是位移,旋轉(zhuǎn)枕赵,顏色變化這三分部疊加組合起來而已猜欺。

原理

所謂轉(zhuǎn)場動畫無非就是從A跳轉(zhuǎn)到B的過程中,通過攔截重寫

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext

在其中我們可以獲得三個視圖:

  • fromView: 轉(zhuǎn)場開始視圖 (即A視圖)
  • toView:轉(zhuǎn)場結(jié)束視圖 (即B視圖)
  • containerView:轉(zhuǎn)場視圖容器,所有轉(zhuǎn)場動畫在里面完成

這樣一來拷窜,我們就能拿到AB視圖里的所有元素和位置开皿,把它放進containerView里,讓A視圖里的元素位移到B視圖里對應(yīng)的位置篮昧,完成轉(zhuǎn)場赋荆,這樣在視覺上就無縫銜接了

思路

有了理論基礎(chǔ)我們再來談思路,我們可以把這個動畫先拆成兩部分懊昨,即A到B窄潭,B到A兩部分,然后A到B可以可以有 點擊Cell變小 --> 松手Cell回復(fù)正常 --> 轉(zhuǎn)場酵颁,A的元素位移到B里對應(yīng)的位置 --> A到B轉(zhuǎn)場完成嫉你。
B到A差點多也是 模糊背景 --> 縮小A視圖 --> 轉(zhuǎn)場,B的元素位移到A李對應(yīng)的位置 --> B到A轉(zhuǎn)場完成躏惋,其實都大同小異幽污。

讓cell變小再正常一是為了給用戶增加反饋,二是這樣才符合運動規(guī)律簿姨,具體可以百度運動規(guī)律距误,要做好動畫對物體的運動規(guī)律還是要有所了解,不然動畫會十分生硬

實現(xiàn)

  • 頁面

先搭建出想要跳轉(zhuǎn)的兩個頁面,這個比較簡單深寥,所以這里就不寫了攘乒,重點還是放在動畫上,不過要注意惋鹅,做動畫推薦用frame布局。

20190726113746.jpg
  • 動畫拆解
    根據(jù)上面拆解的動畫步驟殉簸,一個個來實現(xiàn)
點擊Cell變小
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath  {
     _selectIndexPath = indexPath;
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    [UIView animateWithDuration:0.3 animations:^{
        cell.transform = CGAffineTransformMakeScale(0.8, 0.8);
    }];
    return YES;
}
松開回復(fù)正常
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    if ([_selectIndexPath isEqual:indexPath]) {
        [UIView animateWithDuration:0.3 animations:^{
            cell.transform = CGAffineTransformMakeScale(1, 1);
            return;
        }];
    }
}

轉(zhuǎn)場闰集,A的元素位移到B里對應(yīng)的位置(重點)

我們先設(shè)置代理 self.navigationController.delegate = self; 實現(xiàn)代理

-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    return self;
}
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 1.0f;
}

當點擊跳轉(zhuǎn)時,會進入animateTransition:方法

// 修改轉(zhuǎn)場動畫
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    //獲取的轉(zhuǎn)場視圖容器
    UIView *containerView = [transitionContext containerView];
    //獲取的B控制器
    GSInfoViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //獲取b視圖的背景圖 這里需要注意般卑,我是將b視圖bg上lb的標簽全部添加在bgImageView上武鲁,所以做動畫時只拿到bgImageView就行了;
    UIView *toBgImageView = toVC.bgImageView;
    toBgImageView.hidden = YES;
    toVC.view.alpha = 0;
    //將B添加到轉(zhuǎn)場視圖容器里
    [containerView addSubview:toVC.view];
     
    //獲取對應(yīng)的A頁面的cell
    GSTodayTableViewCell *cell = (GSTodayTableViewCell *)[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
    //獲取A頁面的各個元素
    //創(chuàng)建A頁面的cell的背景蝠检,設(shè)置frame
    UIView *fromBgImageView = [[UIImageView alloc]initWithImage:cell.bgImageView.image];
    fromBgImageView.frame = [containerView convertRect:cell.bgImageView.frame fromView:cell.bgImageView.superview];
    //創(chuàng)建A頁面的cell的標簽沐鼠,設(shè)置frame
    UILabel *typeL = [self copyLabel:cell.typeL];
    [fromBgImageView addSubview:typeL];
    //創(chuàng)建A頁面的cell的標題,設(shè)置frame
    UILabel *contentLabel =[self copyLabel:cell.contentL];
    [fromBgImageView addSubview:contentLabel];
    //創(chuàng)建關(guān)閉按鈕叹谁,雖然A頁面沒有該元素饲梭,但動畫時會出現(xiàn),這里的frame可以根據(jù)自己感覺調(diào)整
    UIButton *closeBtn = [UIButton buttonWithType:0];
    closeBtn.x = cell.width - 2*kMargin - 60;
    closeBtn.centerY = kHeight(20)-10;
    closeBtn.size = CGSizeMake(40, 40);
    closeBtn.alpha = 0.6;
    [closeBtn setBackgroundImage:[UIImage imageNamed:@"close"] forState:UIControlStateNormal];
    [fromBgImageView addSubview:closeBtn];
    //將A添加到轉(zhuǎn)場視圖容器里,注意焰檩,需要先添加B在添加A憔涉,應(yīng)該A是在前面做動畫的
    [containerView addSubview:fromBgImageView];
    
    //這里可以設(shè)置動畫時間,彈性等
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0f usingSpringWithDamping:0.6f initialSpringVelocity:1.0f options:UIViewAnimationOptionCurveLinear animations:^{
        [containerView layoutIfNeeded];
        toVC.view.alpha = 1.0f;
        //這里將B頁面各個元素的frame給A頁面對應(yīng)的
        typeL.frame = toVC.typeL.frame ;
        closeBtn.frame = toVC.closeBtn.frame;
        contentLabel.frame = toVC.titleL.frame;
        fromBgImageView.frame = [containerView convertRect:toBgImageView.frame fromView:toBgImageView.superview];
    } completion:^(BOOL finished) {
        //都是為了動畫弄出來的工具對象析苫,全部刪了隱藏了
        toBgImageView.hidden = NO;
        closeBtn.hidden = YES;
        [fromBgImageView removeFromSuperview];
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
}

上面用到的復(fù)制一個完整的label用方法

- (UILabel *)copyLabel:(UILabel *)label {
    UILabel *newLabel = [[UILabel alloc] init];
    newLabel.text = label.text;
    newLabel.font = label.font;
    newLabel.alpha = label.alpha;
    newLabel.frame =  label.frame;
    newLabel.textColor = label.textColor;
    newLabel.textAlignment = label.textAlignment;
    return newLabel;
}

這樣從A跳轉(zhuǎn)到B的過程場動畫就完成了


接下做從B到A跳轉(zhuǎn)的過程兜叨,其實差別并不大,一樣的設(shè)置代理self.navigationController.delegate = self;衩侥,再加個背景毛玻璃

    // 背景毛玻璃
    UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
    UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
    effectView.frame = CGRectMake(0, 0, kScreenW, kScreenH);
    [self.view addSubview:effectView];

實現(xiàn)代理

-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    return self;
}
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
    return 1.0f;
}

點擊跳轉(zhuǎn)進入animateTransition:

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    UIView *containerView = [transitionContext containerView];
    //獲取跳轉(zhuǎn)后的頁面
    GSTodayViewController *toVC = (GSTodayViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
    //根據(jù)selectIndexPath獲取對應(yīng)的cell, selectIndexPath可以在跳轉(zhuǎn)的時候傳進來
    GSTodayTableViewCell *cell = (GSTodayTableViewCell *)[toVC.tableView cellForRowAtIndexPath:self.selectIndexPath];
    //獲取cell的背景
    UIView *cellBgImageView = cell.bgImageView;
    cellBgImageView.hidden = YES;
    
    //獲取當前頁面
    GSInfoViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    //獲取當前的背景圖
    UIView *bgImageView = fromVC.bgImageView;
    bgImageView.hidden = YES;
    //創(chuàng)建背景圖做動畫用
    UIView *screenshotView = [bgImageView snapshotViewAfterScreenUpdates:NO];
    //圓角與cell保持一致
    screenshotView.layer.masksToBounds = YES;
    screenshotView.layer.cornerRadius = 15;
    screenshotView.frame = [containerView convertRect:bgImageView.frame fromView:bgImageView.superview];
    //創(chuàng)建標簽
    UILabel *typeL = [self copyLabel:self.typeL];
    [screenshotView addSubview:typeL];
    //創(chuàng)建標題 到這里可以看出這個剛剛的一模一樣
    UILabel *titleL =  [self copyLabel:self.titleL];
    [screenshotView addSubview:titleL];
    [containerView addSubview:screenshotView];
    [containerView insertSubview:toVC.view belowSubview:fromVC.view];
    //動畫
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5f initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        [containerView layoutIfNeeded];
        //隱藏當前視圖
        fromVC.view.alpha = 0.0f;
        //轉(zhuǎn)場過程中保持視圖圓角
        screenshotView.layer.cornerRadius = 15;
        self.tableView.frame = CGRectMake(self.tableView.frame.origin.x, self.tableView.frame.origin.y, self.tableView.frame.size.width,kScreenW*1.3*0.8);
        self.tableView.layer.cornerRadius = 15;
        screenshotView.frame = [containerView convertRect:cellBgImageView.frame fromView:cellBgImageView.superview];
        //這里需要設(shè)置label的frame,與外層cell的位置保持一致就行国旷,外層cell是里的元素frame是寫死的,我這里也就一樣寫死
        typeL.x = kWidth(16);
        typeL.y = kHeight(20);
        titleL.x = kWidth(16);
        titleL.y = kHeight(20)+ typeL.height + 8;
        _closeBtn.alpha = 0;
    } completion:^(BOOL finished) {
        //一樣茫死,動畫結(jié)束后該隱藏的隱藏跪但,該刪除的刪除
        bgImageView.hidden = YES;
        [screenshotView removeFromSuperview];
        cellBgImageView.hidden = NO;
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

這樣一個完整的A跳B,B返A(chǔ)的過場動畫就搞定了璧榄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末特漩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子骨杂,更是在濱河造成了極大的恐慌涂身,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搓蚪,死亡現(xiàn)場離奇詭異蛤售,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門悴能,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揣钦,“玉大人,你說我怎么就攤上這事漠酿》氚迹” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵炒嘲,是天一觀的道長宇姚。 經(jīng)常有香客問我,道長夫凸,這世上最難降的妖魔是什么浑劳? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮夭拌,結(jié)果婚禮上魔熏,老公的妹妹穿的比我還像新娘。我一直安慰自己鸽扁,他們只是感情好蒜绽,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著献烦,像睡著了一般滓窍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巩那,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天吏夯,我揣著相機與錄音,去河邊找鬼即横。 笑死噪生,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的东囚。 我是一名探鬼主播跺嗽,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼页藻!你這毒婦竟也來了桨嫁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤份帐,失蹤者是張志新(化名)和其女友劉穎璃吧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體废境,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡畜挨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年筒繁,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片巴元。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡毡咏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逮刨,到底是詐尸還是另有隱情呕缭,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布修己,位于F島的核電站臊旭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏箩退。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一佳谦、第九天 我趴在偏房一處隱蔽的房頂上張望戴涝。 院中可真熱鬧,春花似錦钻蔑、人聲如沸啥刻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽可帽。三九已至,卻和暖如春窗怒,著一層夾襖步出監(jiān)牢的瞬間映跟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工扬虚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留努隙,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓辜昵,卻偏偏與公主長得像荸镊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子堪置,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345