先看動(dòng)畫,這是仿后的效果

這轉(zhuǎn)場動(dòng)畫,乍看一下很復(fù)雜,其實(shí)是拆化細(xì)分之后是很簡單的。大部分動(dòng)畫都只是位移,旋轉(zhuǎn),顏色變化這三分部疊加組合起來而已。
原理
所謂轉(zhuǎn)場動(dòng)畫無非就是從A跳轉(zhuǎn)到B的過程中,通過攔截重寫
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
在其中我們可以獲得三個(gè)視圖:
- fromView: 轉(zhuǎn)場開始視圖 (即A視圖)
- toView:轉(zhuǎn)場結(jié)束視圖 (即B視圖)
- containerView:轉(zhuǎn)場視圖容器,所有轉(zhuǎn)場動(dòng)畫在里面完成
這樣一來,我們就能拿到AB視圖里的所有元素和位置,把它放進(jìn)containerView里,讓A視圖里的元素位移到B視圖里對應(yīng)的位置,完成轉(zhuǎn)場,這樣在視覺上就無縫銜接了
思路
有了理論基礎(chǔ)我們再來談思路,我們可以把這個(gè)動(dòng)畫先拆成兩部分,即A到B,B到A兩部分,然后A到B可以可以有 點(diǎn)擊Cell變小 --> 松手Cell回復(fù)正常 --> 轉(zhuǎn)場,A的元素位移到B里對應(yīng)的位置 --> A到B轉(zhuǎn)場完成。
B到A差點(diǎn)多也是 模糊背景 --> 縮小A視圖 --> 轉(zhuǎn)場,B的元素位移到A李對應(yīng)的位置 --> B到A轉(zhuǎn)場完成,其實(shí)都大同小異。
讓cell變小再正常一是為了給用戶增加反饋,二是這樣才符合運(yùn)動(dòng)規(guī)律,具體可以百度運(yùn)動(dòng)規(guī)律,要做好動(dòng)畫對物體的運(yùn)動(dòng)規(guī)律還是要有所了解,不然動(dòng)畫會(huì)十分生硬
實(shí)現(xiàn)
- 頁面
先搭建出想要跳轉(zhuǎn)的兩個(gè)頁面,這個(gè)比較簡單,所以這里就不寫了,重點(diǎn)還是放在動(dòng)畫上,不過要注意,做動(dòng)畫推薦用frame布局。

- 動(dòng)畫拆解
根據(jù)上面拆解的動(dòng)畫步驟,一個(gè)個(gè)來實(shí)現(xiàn)
點(diǎ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)的位置(重點(diǎn))
我們先設(shè)置代理 self.navigationController.delegate = self; 實(shí)現(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;
}
當(dāng)點(diǎn)擊跳轉(zhuǎn)時(shí),會(huì)進(jìn)入animateTransition:方法
// 修改轉(zhuǎn)場動(dòng)畫
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
//獲取的轉(zhuǎn)場視圖容器
UIView *containerView = [transitionContext containerView];
//獲取的B控制器
GSInfoViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//獲取b視圖的背景圖 這里需要注意,我是將b視圖bg上lb的標(biāo)簽全部添加在bgImageView上,所以做動(dòng)畫時(shí)只拿到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頁面的各個(gè)元素
//創(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的標(biāo)簽,設(shè)置frame
UILabel *typeL = [self copyLabel:cell.typeL];
[fromBgImageView addSubview:typeL];
//創(chuàng)建A頁面的cell的標(biāo)題,設(shè)置frame
UILabel *contentLabel =[self copyLabel:cell.contentL];
[fromBgImageView addSubview:contentLabel];
//創(chuàng)建關(guān)閉按鈕,雖然A頁面沒有該元素,但動(dòng)畫時(shí)會(huì)出現(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是在前面做動(dòng)畫的
[containerView addSubview:fromBgImageView];
//這里可以設(shè)置動(dòng)畫時(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頁面各個(gè)元素的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) {
//都是為了動(dòng)畫弄出來的工具對象,全部刪了隱藏了
toBgImageView.hidden = NO;
closeBtn.hidden = YES;
[fromBgImageView removeFromSuperview];
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
上面用到的復(fù)制一個(gè)完整的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的過程場動(dòng)畫就完成了
接下做從B到A跳轉(zhuǎn)的過程,其實(shí)差別并不大,一樣的設(shè)置代理self.navigationController.delegate = self;,再加個(gè)背景毛玻璃
// 背景毛玻璃
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
effectView.frame = CGRectMake(0, 0, kScreenW, kScreenH);
[self.view addSubview:effectView];
實(shí)現(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;
}
點(diǎn)擊跳轉(zhuǎn)進(jì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)的時(shí)候傳進(jìn)來
GSTodayTableViewCell *cell = (GSTodayTableViewCell *)[toVC.tableView cellForRowAtIndexPath:self.selectIndexPath];
//獲取cell的背景
UIView *cellBgImageView = cell.bgImageView;
cellBgImageView.hidden = YES;
//獲取當(dāng)前頁面
GSInfoViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//獲取當(dāng)前的背景圖
UIView *bgImageView = fromVC.bgImageView;
bgImageView.hidden = YES;
//創(chuàng)建背景圖做動(dò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)建標(biāo)簽
UILabel *typeL = [self copyLabel:self.typeL];
[screenshotView addSubview:typeL];
//創(chuàng)建標(biāo)題 到這里可以看出這個(gè)剛剛的一模一樣
UILabel *titleL = [self copyLabel:self.titleL];
[screenshotView addSubview:titleL];
[containerView addSubview:screenshotView];
[containerView insertSubview:toVC.view belowSubview:fromVC.view];
//動(dòng)畫
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.5f initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[containerView layoutIfNeeded];
//隱藏當(dāng)前視圖
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) {
//一樣,動(dòng)畫結(jié)束后該隱藏的隱藏,該刪除的刪除
bgImageView.hidden = YES;
[screenshotView removeFromSuperview];
cellBgImageView.hidden = NO;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
這樣一個(gè)完整的A跳B,B返A(chǔ)的過場動(dòng)畫就搞定了。