首先我们需要实现TransDelegate类中的如下两个函数:
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
return [AniObject new];
}
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator{
//遵守了UIViewControllerInteractiveTransitioning协议的对象
return self.object;
}
UIViewControllerInteractiveTransitioning协议用来处理可交互的转场动画的具体表现,需要注意,因为使用的是可交互的转场动画,UIViewControllerAnimatedTransitioning协议中的animateTransition:方法可以空实现。下面我们再创建一个遵守UIViewControllerInteractiveTransitioning协议的类,比如命名为IntObject,上面代码中的self.object即是这个类的示例,IntObject.h文件如下:
@interface IntObject : NSObject<UIViewControllerInteractiveTransitioning>
-(void)updateAniProgress:(CGFloat)progress;
-(void)finish;
-(void)cancel;
@end
IntObject.m文件实现如下:
@interface IntObject()
@property(nonatomic,strong)id<UIViewControllerContextTransitioning> context;
@end
@implementation IntObject
//这个函数用来保存transitionContext
-(void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
self.context = transitionContext;
//跳转的界面
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//最终的位置
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
//添加到内容视图
[[transitionContext containerView]insertSubview:toVC.view belowSubview:fromVC.view];
}
//更新动画状态
-(void)updateAniProgress:(CGFloat)progress{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
//最终的位置
CGRect fR = CGRectMake( [UIScreen mainScreen].bounds.size.width*progress, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
frameVC.frame = fR;
}
//结束转场
-(void)finish{
[UIView animateWithDuration:0.2 animations:^{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
frameVC.frame = CGRectMake([UIScreen mainScreen].bounds.size.width, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
} completion:^(BOOL finished) {
[self.context completeTransition:YES];
}];
}
//取消转场
-(void)cancel{
[UIView animateWithDuration:0.2 animations:^{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
frameVC.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
} completion:^(BOOL finished) {
[self.context cancelInteractiveTransition];
}];
}
@end
下面我们来添加手势,在ViewController2类中添加如下代码:
@interface ViewController2 ()
@property(nonatomic,strong)UIPanGestureRecognizer * pan;
@end
@implementation ViewController2
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
[self.view addGestureRecognizer:self.pan];
// Do any additional setup after loading the view.
}
-(void)pan:(UIPanGestureRecognizer *)pan{
CGPoint translatedPoint = [pan translationInView:self.view];
CGFloat persent = translatedPoint.x / [[UIScreen mainScreen]bounds].size.width;
if (persent<0) {
return;
}
persent = fabs(persent);
IntObject * obj = [(TransDelegate *)self.transitioningDelegate object];
switch (pan.state) {
case UIGestureRecognizerStateBegan:{
[self dismissViewControllerAnimated:YES completion:nil];
break;
}
case UIGestureRecognizerStateChanged:{
//手势过程中,通过updateInteractiveTransition设置pop过程进行的百分比
[obj updateAniProgress:persent];
break;
}
case UIGestureRecognizerStateEnded:{
//手势完成后结束标记并且判断移动距离是否过半,过则finishInteractiveTransition完成转场操作,否者取消转场操作
if (persent > 0.5) {
[obj finish];
}else{
[obj cancel];
}
break;
}
default:
break;
}
}
-(UIPanGestureRecognizer *)pan{
if (!_pan) {
_pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)];
}
return _pan;
}
@end
手势效果如下:
其实,上面演示的是我们自己创建了一个类来实现UIViewControllerInteractiveTransitioning协议,其实系统也为我们提供一个类:UIPercentDrivenInteractiveTransition类,我们可以直接调用这个类的如下3个函数而不需要我们自己重写了,但是必须实现UIViewControllerAnimatedTransitioning协议中的transitionContext函数来实现动画效果。
- (void)updateInteractiveTransition:(CGFloat)percentComplete;
- (void)cancelInteractiveTransition;
- (void)finishInteractiveTransition;
其实现原理与我们上面进行完全的自定义是一样的。
三、导航转场动画的自定义
导航转场动画的原理与模态跳转转场动画的原理基本是一致的,不同的我们需要设置UINavigationController实例的delegate为遵守UINavigationControllerDelegate协议的类对象。之后实现如下两个函数:
//设置转场的动画不论是push或pop 返回nil 则使用系统默认的导航转场动画
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
NSLog(@"sss");
return nil;
}
//设置可交互的转场动画
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController{
NSLog(@"aaa");
return nil;
}
可以看到 animationControllerForOperation:函数依然需要返回一个遵守了UIViewControllerAnimatedTransitioning协议的对象,使用方式和前面所介绍的模态跳转自定义转场一模一样。UINavigationControllerOperation这个枚举将告知开发者导航所做的操作,如下:
typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {
UINavigationControllerOperationNone, //无
UINavigationControllerOperationPush, //push操作
UINavigationControllerOperationPop, //pop操作
};
实现UIViewControllerInteractiveTransitioning协议如下:
@interface IntObject()
@property(nonatomic,strong)id<UIViewControllerContextTransitioning> context;
@end
@implementation IntObject
-(void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
self.context = transitionContext;
//跳转的界面
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//最终的位置
toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
//添加到内容视图
[[transitionContext containerView]insertSubview:toVC.view belowSubview:fromVC.view];
}
-(void)updateAniProgress:(CGFloat)progress{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
//最终的位置
CGRect fR = CGRectMake( [UIScreen mainScreen].bounds.size.width*progress, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
frameVC.frame = fR;
[self.context updateInteractiveTransition:progress];
}
-(void)finish{
[UIView animateWithDuration:0.2 animations:^{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
frameVC.frame = CGRectMake([UIScreen mainScreen].bounds.size.width, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
} completion:^(BOOL finished) {
[self.context finishInteractiveTransition];
[self.context completeTransition:YES];
}];
}
-(void)cancel{
[UIView animateWithDuration:0.2 animations:^{
UIView *frameVC = [self.context viewForKey:UITransitionContextFromViewKey];
frameVC.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height);
} completion:^(BOOL finished) {
[self.context cancelInteractiveTransition];
[self.context completeTransition:NO];
}];
}
@end
如此即可以轻松实现可交互的自定义导航动画。
四、UITabBarController的转场动画
UITabbar也可以进行转场动画的自定义,需要设置UITabBarController的delegate并实现协议中的如下两个函数:
//设置非交互的转场动画
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
}
//设置交互的转场动画
- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController{
}
这两个函数的应用和导航自定义动画基本是一致的,这里就不再列举代码,简单的效果见下图: