自定义转场动画步骤:
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;
}