废话不多说,先看效果
在写代码前,先让我们了解一下我们需要的组件
iOS7自定义视图控制器转场的API都是以协议的方式提供的,这样我们可以很简单的将他们插入到我们的类中.最重要的五个组件
1.动画控制器(Animation Controller)遵从UIViewControllerAnimationTransitioning协议,并且负责动画的执行
2.交互控制器(Interaction Controller)通过遵从UIViewControllerInteractiveTransitioning协议控制可交互式转场
3.转场代理(Transitioning Delegates)根据不同的转场类型方便的提供需要的动画控制器和交互控制器
4.转场上下文(Transitioning Contexts)定义了转场时需要的元数据,比如在转场过程中所参与的视图控制器和视图的相关属性.转场上下文对象遵从UIViewControllerContextTransitioning协议,并且由系统负责生成和提供的
5.转场协调器(Transition Coordinators)可以在运行转场动画时,并行的运行其他动画.转场协调器遵从UIViewControllerTransitionCoordinator协议
当我们添加转场动画时,我们要遵从UIViewControllerAnimatedTransitioning协议的动画控制器协议.协议中有三个方法,其他前两个是必须的
//动画执行的时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext;
//动画的效果
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext;
//动画结束后执行
- (void)animationEnded:(BOOL)transitionCompleted;
创建动画的三步:1.创建动画控制器 2.创建转场上下文 3)触发动画实行
核心代码:
#import "PinInvertTransition.h"
#import "ViewController.h"
#import "SecondViewController.h"
@interface PinInvertTransition()
@property(nonatomic,strong)id<UIViewControllerContextTransitioning>transitionContext;
@end
@implementation PinInvertTransition
//动画执行时间
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext{
return 0.7f;
}
//切换动画的时候将调用此方法,我们对于切换时的UIView的设置和动画都在此方法里面
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
self.transitionContext = transitionContext;
//获取两个VC
SecondViewController *fromVC = (SecondViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController *toVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
pragma mark ---转场上下文
UIView *containerView = [transitionContext containerView];
UIButton *button = toVC.button;
[containerView addSubview:toVC.view];
[containerView addSubview:fromVC.view];
pragma Mark----动画的实现
UIBezierPath *finalPath = [UIBezierPath bezierPathWithOvalInRect:button.frame];
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(button.center.x - 0, button.center.y - CGRectGetMaxY(toVC.view.bounds)+30);
}else{
//第四象限
finalPoint = CGPointMake(button.center.x - 0, button.center.y - 0);
}
}else{
if (button.frame.origin.y < (toVC.view.bounds.size.height / 2)) {
//第二象限
finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - CGRectGetMaxY(toVC.view.bounds)+30);
}else{
//第三象限
finalPoint = CGPointMake(button.center.x - CGRectGetMaxX(toVC.view.bounds), button.center.y - 0);
}
}
CGFloat radius = sqrt(finalPoint.x * finalPoint.x + finalPoint.y * finalPoint.y);
//返回一个与button共中心点的圆形,radius是圆的最大的半径
UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)];
pragma mark ---mask的创建,并为mask创建动画
//mask:遮罩,mask是一个CALayer.当我们使用的时候,就需要单独创建一个CALayer作为mask
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = finalPath.CGPath;
fromVC.view.layer.mask = maskLayer;
pragma mark--设置基本动画
CABasicAnimation *pingAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
pingAnimation.fromValue = (__bridge id)(startPath.CGPath);
pingAnimation.toValue = (__bridge id)(finalPath.CGPath);
//持续时间为转场动画执行的时间
pingAnimation.duration = [self transitionDuration:transitionContext];
//时间函数
pingAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
pingAnimation.delegate = self;
//遮罩
[maskLayer addAnimation:pingAnimation forKey:@"pingInvert"];
}
pragma mark ---代理的方法
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
[self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]];
[self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
[self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
}