系统转场动画
CATransition *transition = [CATransition animation];
transition.duration = 0.7;
transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
// 过渡动画种类
//淡化、推挤、揭开、覆盖{kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom}
//立方体、吸收、翻转、波纹、翻页、反翻页、镜头开、镜头关{@"cube",@"suckEffect",@"oglFlip",@"rippleEffect",@"pageCurl",@"pageUnCurl",@"cameraIrisHollowOpen",@"cameraIrisHollowClose"}
transition.type = @"cube";
// 过渡方向
transition.subtype = kCATransitionFromLeft;//{kCATransitionFromLeft, kCATransitionFromRight, kCATransitionFromTop, kCATransitionFromBottom};
// 哪个控制器实现转场,哪个控制器添加动画
[self.navigationController.view.layer addAnimation:transition forKey:nil];
转场动画的使用准则就是:Push/Pop 时重载UINavigationControllerDelegate 的⽅法; Modal 时重载UIViewControllerTransitioningDelegate 的⽅法
UINavigationControllerDelegate (Push/Pop)
在需要实现动画的控制器中,实现 navigationController:animationControllerForOperation:fromViewController:toViewController:函数并返回一个遵守UIViewControllerAnimatedTransitioning协议的NSObject对象,如果Push/Pop都要添加动画,那么通过operation == UINavigationControllerOperationPush 、operation == UINavigationControllerOperationPop 区分,并返回不同的对象
-
UIViewControllerContextTransitioning
- 保存转场上下文,动画结束后移除遮罩
- 编辑转场上下文containerView
- 通过核心动画path,设置两个遮罩的值(跳转按钮遮罩、可以覆盖整个屏幕的遮罩)
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext{
return 0.7f;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
// 保存转场上下文
self.transitionContext = transitionContext;
// 根据上下文的Key获取发生转场的两个控制器
ViewController * fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
SecondViewController *toVC = (SecondViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 获取上下文containerView
UIView *contView = [transitionContext containerView];
[contView addSubview:fromVC.view];
[contView addSubview:toVC.view];
UIButton *button = fromVC.button;
UIBezierPath *maskStartBP = [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(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));
UIBezierPath *maskFinalBP = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)];
//创建一个 CAShapeLayer 来负责展示圆形遮盖
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = maskFinalBP.CGPath; //将它的 path 指定为最终的 path 来避免在动画完成后会回弹
toVC.view.layer.mask = maskLayer;
CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
maskLayerAnimation.fromValue = (__bridge id)(maskStartBP.CGPath);
maskLayerAnimation.toValue = (__bridge id)((maskFinalBP.CGPath));
maskLayerAnimation.duration = [self transitionDuration:transitionContext];
maskLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
maskLayerAnimation.delegate = self;
[maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
}
- CAAnimationDelegate
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
[self.transitionContext completeTransition:![self. transitionContext transitionWasCancelled]];
//清除 fromVC 的 mask
[self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
[self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
}
网友评论