- Transition动画的具体实现步骤
- 界面布局如图所示
*
ViewController
的具体代码如下
```objc
// ViewController.h
#import
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIButton *button;
@end
// ViewController.m
#import "ViewController.h"
#import "ZQTransition.h"
@implementation ViewController
-(void)viewWillAppear:(BOOL)animated {
self.navigationController.delegate = self;
}
#pragma mark ------------------
#pragma mark - UINavigationControllerDelegate
-(id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPush) {
ZQTransition *transition = [ZQTransition new];
return transition;
} else {
return nil;
}
}
@end
```
* `ZQSecondViewController`的具体代码如下
```objc
// ZQSecondViewController.h
#import
@interface ZQSecondViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIButton *button;
@end
// ZQSecondViewController.m
#import "ZQSecondViewController.h"
#import "ViewController.h"
#import "ZQTransition.h"
#import "ZQInvertTransition.h"
@implementation ZQSecondViewController {
UIPercentDrivenInteractiveTransition *percentTransition;
}
-(void)viewWillAppear:(BOOL)animated {
self.navigationController.delegate = self;
}
-(void)viewDidLoad {
[super viewDidLoad];
UIScreenEdgePanGestureRecognizer *edgeGes = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePan:)];
edgeGes.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:edgeGes];
}
-(id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id)animationController {
return percentTransition;
}
-(void)edgePan:(UIPanGestureRecognizer *)recongnizer {
CGFloat per = [recongnizer translationInView:self.view].x / (self.view.bounds.size.width);
per = MIN(1.0, MAX(0.0, per));
if (recongnizer.state == UIGestureRecognizerStateBegan) {
percentTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
[self.navigationController popViewControllerAnimated:YES];
} else if (recongnizer.state == UIGestureRecognizerStateChanged) {
[percentTransition updateInteractiveTransition:per];
} else if (recongnizer.state == UIGestureRecognizerStateEnded || recongnizer.state == UIGestureRecognizerStateCancelled) {
if (per > 0.3) {
[percentTransition finishInteractiveTransition];
} else {
[percentTransition cancelInteractiveTransition];
}
percentTransition = nil;
}
}
-(id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPop) {
ZQInvertTransition *invert = [ZQInvertTransition new];
return invert;
} else {
return nil;
}
}
-(IBAction)popClicked:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
@end
```
* `ZQTransition`的具体代码如下
```objc
// ZQTransition.h
#import
#import
@interface ZQTransition : NSObject
@end
// ZQTransition.m
#import "ZQTransition.h"
#import "ViewController.h"
#import "ZQSecondViewController.h"
@interface ZQTransition()
/** 转场上下文 */
@property(nonatomic, strong)id transitionContext;
@end
@implementation ZQTransition
#pragma mark ------------------
#pragma mark - Protocol
-(NSTimeInterval)transitionDuration:(id)transitionContext {
return 0.7f;
}
-(void)animateTransition:(id)transitionContext {
self.transitionContext = transitionContext;
ViewController *fromVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ZQSecondViewController *toVC = (ZQSecondViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *contView = [transitionContext containerView];
UIButton *button = fromVC.button;
UIBezierPath *maskStartBP = [UIBezierPath bezierPathWithOvalInRect:button.frame];
[contView addSubview:fromVC.view];
[contView addSubview:toVC.view];
// 创建两个圆形的UIBezierPath实例
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];
// 将它的path指定为最终的path来避免在动画完成后会回弹
maskLayer.path = maskFinalBP.CGPath;
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"];
}
#pragma mark ------------------
#pragma mark - 基本动画的代理方法
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
// 通知系统转场动画已经完成
[self.transitionContext completeTransition:![self.transitionContext transitionWasCancelled]];
// 清除fromVC 的mask
[self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
// 清除toView 的mask
[self .transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
}
@end
```
* `ViewController`的具体代码如下
```objc
// ZQInvertTransition.h
#import
#import
@interface ZQInvertTransition : NSObject
@end
// ZQInvertTransition.m
#import "ZQInvertTransition.h"
#import "ViewController.h"
#import "ZQSecondViewController.h"
@interface ZQInvertTransition()
/** 转场动画的上下文 */
@property(nonatomic, strong)id transitionContext;
@end
@implementation ZQInvertTransition
#pragma mark ------------------
#pragma mark - Protocol
-(NSTimeInterval)transitionDuration:(id)transitionContext {
return 0.7f;
}
-(void)animateTransition:(id)transitionContext {
self.transitionContext = transitionContext;
ZQSecondViewController *fromVC = (ZQSecondViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController *toVC = (ViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
UIButton *button = toVC.button;
[containerView addSubview:toVC.view];
[containerView addSubview:fromVC.view];
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);
UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = finalPath.CGPath;
fromVC.view.layer.mask = maskLayer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.fromValue = (__bridge id)(startPath.CGPath);
animation.toValue = (__bridge id)(finalPath.CGPath);
animation.duration = [self transitionDuration:transitionContext];
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.delegate = self;
[maskLayer addAnimation:animation forKey:@"zqInvert"];
}
-(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;
}
@end
```
- 运行结果如图所示