iOS动画效果八:实现类似系统的测滑返回效果

这篇文章主要介绍怎样实现类似系统的测滑返回效果
最终的效果图为:


自定义测滑返回手势.gif

Demo地址为:
Demo地址

对应的实现文件为:
EightViewController、CustomPopGestureViewController、PopAnimation

关于转场动画,下面这张图将的很清楚


转场动画的相关类和协议的方法

为了实现类似系统的测滑返回效果,实际上就是pop的自定义返回动画,再加上UIPanGestureRecognizer手势的交互实现,也就是交互性转场动画(因为根据手指滑动的距离,大于一半会pop到上一个浏览器,小于一半会恢复原状)

由上面的图可以看到,想要实现交互性转场动画,需要实现UIViewControllerInteractiveTransitioning,
但是系统有给我们提供一个可交互的百分比动画实现类,即UIPerCententDrivenInteractiveTransition,
在接下来的实现中,我们可以直接使用系统提供的实现类,也可以自定义一个遵守UIViewControllerInteractiveTransitioning协议的类(我们是使用了系统提供的)

首先,我们先把pop动画的自定义给完成
在eightViewController中,搭建基本的界面,可以push到CustomPopGestureViewController中
在eightViewController.h中

@property (nonatomic, strong) UIButton *button;

在eightViewController.m中

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"测滑返回手势测试";
    self.navigationController.navigationBar.translucent = NO;
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.button.center = self.view.center;
    [self.view addSubview:self.button];
}

- (UIButton *)button {
    if (_button) {
        return _button;
    }
    _button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [_button setTitle:@"跳转下一个播放器" forState:UIControlStateNormal];
    [_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_button addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];
    return _button;
}

- (void)buttonClicked {
    NSLog(@"点击了跳转按钮");
    CustomPopGestureViewController *vc = [[CustomPopGestureViewController alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
}

点击按钮,可以push到CustomGestureViewcontroller中

为了实现自定义的测滑返回功能,我们需要禁用系统的测滑返回功能
在CustomViewController中,我们需要添加以下代码禁用

 //禁用系统自带的侧滑返回手势
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;

接下来,先实现pop的自定义动画
在CustomViewController中,需要遵守UINavigationDelegate协议

self.navigationController.delegate = self;

实现相应的协议方法

- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    if (operation == UINavigationControllerOperationPop) {
        NSLog(@"执行了这个方法PopAnimation");
      //popAnimation是我们后面定义的类,用来实现具体的动画效果
        return [[PopAnimation alloc] init];
    }
    return nil;
}

接下来,我们自定义一个类PopAnimation,遵守协议UIViewControllerAnimatedTransitioning
在类中实现相应的协议方法

//设置动画执行的时常
- (NSTimeInterval)transitionDuration:(id)transitionContext {
    return 1.0f;
}

//用来处理具体的动画
- (void)animateTransition:(nonnull id)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //需要将转场后的界面给加上去才行
//    [[transitionContext containerView] addSubview:toVC.view];
    [[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGFloat height = [UIScreen mainScreen].bounds.size.height;
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromVC.view.frame = CGRectMake(width, 0, width, height);
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

上面实现动画的思路:

我们可以从最终效果图上可以看成:
pop动画的过程就是将上面的view向右挪动,直到移出界面
所以代码中有这一句
[[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
将toView的view放在fromVC.view的下面,使用UIview animation来完成移动动画

注意:这里只能使用UIView animation来完成动画,代码中注释的是我最初使用CABasicAnimation来完成移动动画,pop自定义动画可行,但是对于后面使用系统类UIPerCententDrivenInteractiveTransition完成交互不可行,
如果想要使用CaBasicAnimation,必须自定义可交互的百分比动画类

这样我们就完成了pop的自定义动画返回,但是我们需要加入交互,通过判断手指滑动的距离来判断动画的进行程度

接下来,我们需要在CustomPopGestureViewController中,定义相应的手势属性和相应的交互类

@property (nonatomic, strong) UIPanGestureRecognizer *pan;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactiveTransition;

接下来完成相应的手势类的添加

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"类似系统侧滑返回手势实现";
    self.navigationController.navigationBar.translucent = NO;
    self.view.backgroundColor = [UIColor yellowColor];
    
    //禁用系统自带的侧滑返回手势
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    
    [self.view addGestureRecognizer:self.pan];
    
    self.navigationController.delegate = self;
}

- (UIPanGestureRecognizer *)pan {
    if (_pan) {
        return _pan;
    }
    _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    return _pan;
}

在手势类的出发方法中,我们需要根据UIPanGestureRecognizer的状态来初始化或者更新或者取消或者结束交互类

UIGestureRecognizerStateBegan:手势类刚开始出发时,初始化相应的interactiveTransition
UIGestureRecognizerStateChanged:手势继续滑动的时候,交互类进行相应的更新
UIGestureRecognizerStateEnded:手势结束时,交互类结束
UIGestureRecognizerStateCancelled:手势取消时,交互类取消
最后一个手势周期完成后都要将交互类interactiveTransition置为nil

代码为:

- (void)pan:(UIPanGestureRecognizer *)pan {
    if (self.navigationController.childViewControllers.count == 1) {
        return;
    }
    CGPoint point = [pan translationInView:self.view];
    CGFloat percent = point.x / self.view.bounds.size.width;
    NSLog(@"percent的进度值为:%f",percent);
    percent = MIN(MAX(0, percent), 1);
    if (pan.state == UIGestureRecognizerStateBegan) {
        _interactiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
        [self.navigationController popViewControllerAnimated:YES];
    } else if (pan.state == UIGestureRecognizerStateChanged) {
        [_interactiveTransition updateInteractiveTransition:percent];
    } else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
        //手势结束后,若进度大于0.5就完成pop动画,否则取消
        if (percent > 0.5) {
            [_interactiveTransition finishInteractiveTransition];
        } else {
            [_interactiveTransition cancelInteractiveTransition];
        }
        _interactiveTransition = nil;
    }
}

另外,不要忘记,在UINavigationDelegate协议方法中实现可交互类的协议方法

- (id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id)animationController {
    if ([animationController isKindOfClass:[PopAnimation class]]) {
        NSLog(@"执行了这个方法UIPercentDrivenInteractiveTransition");
        return _interactiveTransition;
    }
    return nil;
}

最终的效果图为:


自定义测滑返回手势.gif

总结

最终效果图为:


自定义测滑返回手势.gif

Demo地址为:
Demo地址

iOS开发中动画效果的探究(一)

iOS动画效果的探究二:UIView Animation实现动画

iOS动画效果三:CABAsicAnimation实现平移、旋转和放大

ios动画效果四:使用Pop框架实现弹簧效果

iOS动画效果五:CABasicAnimation实现绕定点旋转的效果]

iOS动画效果六:实现自定义的push转场动画

iOS动画效果七:实现自定义present转场动画效果

iOS动画效果八:实现类似系统的测滑返回效果

你可能感兴趣的:(iOS动画效果八:实现类似系统的测滑返回效果)