iOS如何自定义控制器转场动画push详解

2020-01-21 03:33:14王冬梅

界面搭建好了:

ios,push转场动画,控制器转场动画,自定义转场动画

然后想在Push的时候实现自定义转场动画首先要遵守一个协议UINavigationControllerDelegate

苹果在 UINavigationControllerDelegate 中给出了几个协议方法,通过返回类型就可以很清楚地知道各自的具体作用。


//用来自定义转场动画
- (nullable id)navigationController:(UINavigationController *)navigationController         animationControllerForOperation:(UINavigationControllerOperation)operation            fromViewController:(UIViewController *)fromVC             toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);

//为这个动画添加用户交互
- (nullable id)navigationController:(UINavigationController *)navigationController       interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);

在第一个方法里只要返回一个准守UIViewControllerInteractiveTransitioning协议的对象,并在里面实现动画即可

创建继承自 NSObject 并且声明 UIViewControllerAnimatedTransitioning 的的动画类。 重载 UIViewControllerAnimatedTransitioning 中的协议方法。

//返回动画时间
- (NSTimeInterval)transitionDuration:(nullable id)transitionContext;
//将动画的代码写到里面即可
- (void)animateTransition:(id)transitionContext;

首先我自定义一个名为LRTransitionPushController的类继承于NSObject准守了UIViewControllerAnimatedTransitioning协议


 - (void)animateTransition:(id)transitionContext{  
 self.transitionContext = transitionContext;  
 //获取源控制器 注意不要写成 UITransitionContextFromViewKey
 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //获取目标控制器 注意不要写成 UITransitionContextToViewKey
 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];  
 //获得容器视图
 UIView *containView = [transitionContext containerView];
 // 都添加到container中。注意顺序 目标控制器的view需要后面添加
 [containView addSubview:fromVc.view];
 [containView addSubview:toVc.view];  
 UIButton *button = fromVc.button;
 //绘制圆形
 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
  
 //创建两个圆形的 UIBezierPath 实例;一个是 button 的 size ,另外一个则拥有足够覆盖屏幕的半径。最终的动画则是在这两个贝塞尔路径之间进行的
 //按钮中心离屏幕最远的那个角的点
 CGPoint finalPoint;
 //判断触发点在那个象限
 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)){
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //第一象限
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  }else{
   //第四象限
   finalPoint = CGPointMake(0, 0);
  }
 }else{
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   //第二象限
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  }else{
   //第三象限
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0);
  }
 } 
 CGPoint startPoint = CGPointMake(button.center.x, button.center.y);
 //计算向外扩散的半径 = 按钮中心离屏幕最远的那个角距离 - 按钮半径
 CGFloat radius = sqrt((finalPoint.x-startPoint.x) * (finalPoint.x-startPoint.x) + (finalPoint.y-startPoint.y) * (finalPoint.y-startPoint.y)) - sqrt(button.frame.size.width/2 * button.frame.size.width/2 + button.frame.size.height/2 * button.frame.size.height/2);
 UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)];  
 //赋值给toVc视图layer的mask
 CAShapeLayer *maskLayer = [CAShapeLayer layer];
 maskLayer.path = endPath.CGPath;
 toVc.view.layer.mask = maskLayer;
  
 CABasicAnimation *maskAnimation =[CABasicAnimation animationWithKeyPath:@"path"];
 maskAnimation.fromValue = (__bridge id)startPath.CGPath;
 maskAnimation.toValue = (__bridge id)endPath.CGPath;
 maskAnimation.duration = [self transitionDuration:transitionContext];
 maskAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
 maskAnimation.delegate = self;
 [maskLayer addAnimation:maskAnimation forKey:@"path"]; 
}