先看動畫,這是仿後的效果bash
這轉場動畫,乍看一下很複雜,實際上是拆化細分以後是很簡單的。大部分動畫都只是位移,旋轉,顏色變化這三分部疊加組合起來而已。工具
所謂轉場動畫無非就是從A跳轉到B的過程當中,經過攔截重寫佈局
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
複製代碼
在其中咱們能夠得到三個視圖:動畫
這樣一來,咱們就能拿到AB視圖裏的全部元素和位置,把它放進containerView裏,讓A視圖裏的元素位移到B視圖裏對應的位置,完成轉場,這樣在視覺上就無縫銜接了ui
有了理論基礎咱們再來談思路,咱們能夠把這個動畫先拆成兩部分,即A到B,B到A兩部分,而後A到B能夠能夠有 點擊Cell變小 --> 鬆手Cell回覆正常 --> 轉場,A的元素位移到B裏對應的位置 --> A到B轉場完成。 B到A差點多也是 模糊背景 --> 縮小A視圖 --> 轉場,B的元素位移到A李對應的位置 --> B到A轉場完成,其實都大同小異。spa
######讓cell變小再正常一是爲了給用戶增長反饋,二是這樣才符合運動規律,具體能夠百度運動規律,要作好動畫對物體的運動規律仍是要有所瞭解,否則動畫會十分生硬代理
先搭建出想要跳轉的兩個頁面,這個比較簡單,因此這裏就不寫了,重點仍是放在動畫上,不過要注意,作動畫推薦用frame佈局。code
- (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;
}
複製代碼
- (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;
}];
}
}
複製代碼
咱們先設置代理 self.navigationController.delegate = self;
實現代理orm
-(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;
}
複製代碼
當點擊跳轉時,會進入animateTransition:
方法cdn
// 修改轉場動畫
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
//獲取的轉場視圖容器
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添加到轉場視圖容器裏
[containerView addSubview:toVC.view];
//獲取對應的A頁面的cell
GSTodayTableViewCell *cell = (GSTodayTableViewCell *)[self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]];
//獲取A頁面的各個元素
//建立A頁面的cell的背景,設置frame
UIView *fromBgImageView = [[UIImageView alloc]initWithImage:cell.bgImageView.image];
fromBgImageView.frame = [containerView convertRect:cell.bgImageView.frame fromView:cell.bgImageView.superview];
//建立A頁面的cell的標籤,設置frame
UILabel *typeL = [self copyLabel:cell.typeL];
[fromBgImageView addSubview:typeL];
//建立A頁面的cell的標題,設置frame
UILabel *contentLabel =[self copyLabel:cell.contentL];
[fromBgImageView addSubview:contentLabel];
//建立關閉按鈕,雖然A頁面沒有該元素,但動畫時會出現,這裏的frame能夠根據本身感受調整
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添加到轉場視圖容器裏,注意,須要先添加B在添加A,應該A是在前面作動畫的
[containerView addSubview:fromBgImageView];
//這裏能夠設置動畫時間,彈性等
[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頁面對應的
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];
}];
}
複製代碼
上面用到的複製一個完整的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跳轉到B的過程場動畫就完成了
接下作從B到A跳轉的過程,其實差異並不大,同樣的設置代理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];
複製代碼
實現代理
-(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;
}
複製代碼
點擊跳轉進入animateTransition:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIView *containerView = [transitionContext containerView];
//獲取跳轉後的頁面
GSTodayViewController *toVC = (GSTodayViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
//根據selectIndexPath獲取對應的cell, selectIndexPath能夠在跳轉的時候傳進來
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;
//建立背景圖作動畫用
UIView *screenshotView = [bgImageView snapshotViewAfterScreenUpdates:NO];
//圓角與cell保持一致
screenshotView.layer.masksToBounds = YES;
screenshotView.layer.cornerRadius = 15;
screenshotView.frame = [containerView convertRect:bgImageView.frame fromView:bgImageView.superview];
//建立標籤
UILabel *typeL = [self copyLabel:self.typeL];
[screenshotView addSubview:typeL];
//建立標題 到這裏能夠看出這個剛剛的如出一轍
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;
//轉場過程當中保持視圖圓角
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];
//這裏須要設置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) {
//同樣,動畫結束後該隱藏的隱藏,該刪除的刪除
bgImageView.hidden = YES;
[screenshotView removeFromSuperview];
cellBgImageView.hidden = NO;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
複製代碼
這樣一個完整的A跳B,B返A的過場動畫就搞定了。