自定义Pop转场动画
继续使用上个程序,把push改为pop只需要做很少的工作就能完成
1、复制PushTransition.h和PushTransition.m。命名为PopTransition.h和PopTransition.m
2、在PopTransition.m中把
ViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
SecondViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
改为:
SecondViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
两个类名交换位置即可
3、把所有的avatarImageView改为sourceImageView,把所有的sourceImageView改为avatarImageView,如下:
// 转场动画的具体内容
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
// 获取动画的源控制器和目标控制器
SecondViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView * container = transitionContext.containerView;
// 创建一个imageView的截图,并把原本imageView隐藏,造成以为移动的就是imageView的假象
UIView * snapshotView = [fromVC.avatarImageView snapshotViewAfterScreenUpdates:NO];
snapshotView.frame = [container convertRect:fromVC.avatarImageView.frame fromView:fromVC.view];
fromVC.avatarImageView.hidden = YES;
// 设置目标控制器的位置,并把透明度设为0,在后面的动画中慢慢显示出来变为1
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
toVC.sourceImageView.hidden = YES;
// 都添加到container中。注意顺序
// [container addSubview:toVC.view];
[container insertSubview:toVC.view belowSubview:fromVC.view];
[container addSubview:snapshotView];
// 执行动画
[UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
snapshotView.frame = toVC.sourceImageView.frame;
fromVC.view.alpha = 0;
} completion:^(BOOL finished) {
toVC.sourceImageView.hidden = NO;
[snapshotView removeFromSuperview];
fromVC.avatarImageView.hidden = NO;
//一定要记得动画完成后执行此方法,让系统管理 navigation
[transitionContext completeTransition: ![transitionContext transitionWasCancelled]]; // 如果参数写成yes,当用户取消pop时,会继续执行动画,也就是让detailVC消失,设置成这个参数,会避免这样的错误
}];
}
4、修改ViewController中的判断动画类型的方法,加入pop的判断:
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush){ // 就是在这里判断是哪种动画类型
return [[PushTransition alloc] init]; // 返回push动画的类
}else if (operation == UINavigationControllerOperationPop){ // 就是在这里判断是哪种动画类型
return [[PopTransition alloc] init]; // 返回pop动画的类{
}else{
return nil;
}
}
系统默认的 Push 和 Pop 动画都支持手势驱动,并且可以根据手势移动距离改变动画完成度。幸运的是,Cocoa 已经集成了相关方法,我们只用告诉它百分比就可以了。所以下一步就是手势驱动。
1、在 SecondViewController 的 viewDidLoad() 方法中,加入滑动手势。
// 加入左侧边界手势
UIScreenEdgePanGestureRecognizer * edgePan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePanGesture:)];
edgePan.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:edgePan];
2、遵循UINavigationControllerDelegate协议,因为navigationController的动画需要在这里执行,所以需要设置代理为自己
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.navigationController.delegate = self;
}
3、在手势监听方法中,创建 UIPercentDrivenInteractiveTransition 属性,并实现手势百分比更新。
// 在手势监听方法中,创建UIPercentDrivenInteractiveTransition属性,并实现手势百分比更新
- (void)edgePanGesture:(UIScreenEdgePanGestureRecognizer *)edgePan{
// 进度值,这是左侧边界的算法,如果要改为右侧边界,改为self.view.bounds.size.width / [edgePan translationInView:self.view].x;
CGFloat progress = [edgePan translationInView:self.view].x / self.view.bounds.size.width;
if (edgePan.state == UIGestureRecognizerStateBegan) {
self.percentDrivenTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
}else if(edgePan.state == UIGestureRecognizerStateChanged){
[self.percentDrivenTransition updateInteractiveTransition:progress];
}else if(edgePan.state == UIGestureRecognizerStateCancelled || edgePan.state == UIGestureRecognizerStateEnded){
if(progress > 0.5){
[self.percentDrivenTransition finishInteractiveTransition];
}else{
[self.percentDrivenTransition cancelInteractiveTransition];
}
self.percentDrivenTransition = nil;
}
}
4、实现返回 UIViewControllerInteractiveTransitioning 的方法并返回刚刚创建的 UIPercentDrivenInteractiveTransition属性。
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController{
if([animationController isKindOfClass:[PopTransition class]]){
return self.percentDrivenTransition;
}else{
return nil;
}
}
5、还需要设置一下返回动画,否则手势驱动不会生效
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPop){
return [[PopTransition alloc] init]; // 返回pop动画的类
}else{
return nil;
}
}
完成