美文网首页iOS常用iOS
iOS动画效果八:实现类似系统的测滑返回效果

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

作者: 落叶兮兮 | 来源:发表于2020-11-09 09:43 被阅读0次

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


    自定义测滑返回手势.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<UIViewControllerAnimatedTransitioning>)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<UIViewControllerContextTransitioning>)transitionContext {
        return 1.0f;
    }
    
    //用来处理具体的动画
    - (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)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<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)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动画效果八:实现类似系统的测滑返回效果

        本文链接:https://www.haomeiwen.com/subject/zfoxbktx.html