先看動畫薛夜,這是仿后的效果
這轉(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布局。
- 動畫拆解
根據(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ǔ)的過場動畫就搞定了璧榄。