先看动画,这是仿后的效果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的过场动画就搞定了。