iOS自定义转场动画

3,780 阅读3分钟
iOS7推出了新的转场动画API,以协议id<UIViewControllerInterativeTransition>、id<UIViewAnimatedTransitioning>方式开放给开发者,不同于代理、类别,这样更易于我们自定义动画,更加灵活。下面介绍一下自定义转场动画


自定义转场动画步骤:

1:创建动画管理类ZZFTransition,实现UIViewControllerAnimatedTransitioning,CAAnimationDelegate协议

自定义动画的主要代码都在管理类中实现,在需要动画的时候调用这个类的方法即可。

ZZFTransition.h

#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>

typedef NS_ENUM(NSUInteger, ZZFTransitionType) {    ZZFTransitionTypePush,    ZZFTransitionTypePop,};

@interface ZZFTransition : NSObject<UIViewControllerAnimatedTransitioning,CAAnimationDelegate>+(ZZFTransition *)transitionAnimationWithType:(ZZFTransitionType)type;@end


定义枚举类型ZZFTransitionType,包含两个值,分别是push类型和pop类型。

定义类方法transitionAnimationWithType,传入动画类型,返回实例对象。

ZZFTransition.m

+(ZZFTransition *)transitionAnimationWithType:(ZZFTransitionType)type{
    ZZFTransition *transition = [[ZZFTransition alloc]init];
    transition.type = type;
    return transition;
}



在.m文件中实现transitionAnimationWithType类方法,初始化实例对象,设置动画类型,返回对象。

2:实现UIViewControllerAnimatedTransitioning协议中的方法

-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
    return 3;
}
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    
    self.transitionContext = transitionContext;
    
    switch (_type) {
        case ZZFTransitionTypePush:
        {
            [self pushAnimation:transitionContext];
        }
            break;
            
        case ZZFTransitionTypePop:
        {
            [self popAnimation:transitionContext];
        }
            break;
    }
}



-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext;这个方法返回转场动画执行的时间。

-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext;在这个方法内部实现动画的主要代码。

这里根据创建管理对象的时候传进来的动画类型,来分别处理不同的动画逻辑。把push和pop的动画实现放到一个方法中去,这里的代码更简洁。

- (void)pushAnimation:(id<UIViewControllerContextTransitioning>)transitionContext{
    // 获得即将消失的vc的v
    UIView *fromeView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    // 获得即将出现的vc的v
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    // 获得容器view
    UIView *containerView = [transitionContext containerView];
    [containerView addSubview:fromeView];
    [containerView addSubview:toView];
    UIBezierPath *startBP = [UIBezierPath bezierPathWithOvalInRect:CGRectMake((containerView.frame.size.width)/2, (containerView.frame.size.height)/2, 12, 12)];
    CGFloat radius = containerView.frame.size.height - 100;
    UIBezierPath *finalBP = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(150 - radius, 150 -radius, radius*2, radius*2)];
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.path = finalBP.CGPath;
    toView.layer.mask = maskLayer;
    //执行动画
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.fromValue = (__bridge id _Nullable)(startBP.CGPath);
    animation.toValue = (__bridge id _Nullable)(finalBP.CGPath);
    animation.duration = [self transitionDuration:transitionContext];
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    animation.delegate = self;
    [maskLayer addAnimation:animation forKey:@"path"];
}



参数transitionContext是执行动画的上下文,里面包含动画执行所需要的所有元素。

通过键值的方式,从上下文对象中可以的到需要执行动画的上一个VC和view,下一个VC和view。

UITransitionContextFromViewKey  即将消失的view

UITransitionContextToViewKey  即将出现的view

UITransitionContextFromViewControllerKey  即将消失的控制器

UITransitionContextToViewControllerKey  即将出现的控制器

然后通过上下文的containerView方法可以得到一个容器view,所有的动画都是在这个容器view内进行的,所以需要把fromView和toView都加入到containerView中。

下边用贝塞尔曲线画两个圆,然后用CABasicAnimation执行从小圆到大圆的动画。

到这里一个自定义的push动画就完成了,下边是使用的方法。

3:自定义动画的使用

在需要执行自定义动画的控制器中,遵守UINavigationControllerDelegate协议,并实现-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC;代理方法。

在执行push或pop操作时,这个代理方法会回调,在方法内部拿到跳转的动画类型,然后执行自己的动画代码就可以实现自定义转场动画。

-(id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
    if (operation == UINavigationControllerOperationPush) {
        ZZFTransition *pushTranstion = [ZZFTransition transitionAnimationWithType:ZZFTransitionTypePush];
        return pushTranstion;
    }
    return nil;
}


这里只实现了push,要实现pop自定义动画,同理。

4:注意点

执行push动画后,可以发现一个问题,界面的所有交互失效了,是因为执行动画时控制器view上加了遮罩layer,在动画执行完成后把遮罩去掉就可以了。

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
        [self.transitionContext completeTransition:YES];
    //清除相应控制器视图的mask
    [self.transitionContext viewForKey:UITransitionContextFromViewKey].layer.mask = nil;
    [self.transitionContext viewForKey:UITransitionContextToViewKey].layer.mask = nil;
}