zoukankan      html  css  js  c++  java
  • App开发流程之右滑返回手势功能续

    上一篇记录了利用系统私有变量和方法实现右滑返回手势功能:http://www.cnblogs.com/ALongWay/p/5893515.html

    这篇继续记录另一种方案:利用UINavigationController的delegate方法。

    核心代理方法有如下两个:

    - (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                              interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);
    
    - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                       animationControllerForOperation:(UINavigationControllerOperation)operation
                                                    fromViewController:(UIViewController *)fromVC
                                                      toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);

    第一个代理方法,要求在视图控制器变换时候,返回一个实现UIViewControllerInteractiveTransitioning协议的对象,该对象用于控制交互进度和状态。

    UIKit.h/UIViewControllerTransitioning.h中已经准备好了一个这样的对象,名为UIPercentDrivenInteractiveTransition。

    主要关注其如下三个方法:

    - (void)updateInteractiveTransition:(CGFloat)percentComplete;//控制交互进度的百分比

    - (void)cancelInteractiveTransition;//中途取消交互

    - (void)finishInteractiveTransition;//完成交互

    可以在一个实现了代理方法的类中,定义一个手势目标方法来调用上述方法,达到控制作为变量的UIPercentDrivenInteractiveTransition对象的目的。

    主要代码如下:

    - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
    {
        if ([animationController isKindOfClass:[PopAnimation class]])
            return _interactivePopTransition;
        
        return nil;
    }
    
    - (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
    {
        if (operation == UINavigationControllerOperationPop)
            return _popAnimation;
        
        return nil;
    }
    
    - (void)handlePopGestureOperation:(UIPanGestureRecognizer *)recognizer{
        //将手指在屏幕上的移动距离与屏幕宽度比例作为动画的进度
        CGPoint translationInScreen = [recognizer translationInView:[UIApplication sharedApplication].keyWindow];
        CGFloat progress = translationInScreen.x / [UIScreen mainScreen].bounds.size.width;
        progress = MIN(1.0, MAX(0.0, progress));
        
        if (recognizer.state == UIGestureRecognizerStateBegan) {
            //新建手势交互对象
            _interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
    
            //开始执行pop动画
            [_naviController popViewControllerAnimated:YES];
        }else if (recognizer.state == UIGestureRecognizerStateChanged) {
             //更新进度
            [_interactivePopTransition updateInteractiveTransition:progress];
        }else if (recognizer.state == UIGestureRecognizerStateEnded
                  || recognizer.state == UIGestureRecognizerStateCancelled) {
            //手势结束时如果进度大于一半,那么就完成pop操作,否则取消
            if (progress > 0.5) {
                [_interactivePopTransition finishInteractiveTransition];
            }else {
                [_interactivePopTransition cancelInteractiveTransition];
            }
            
            //手势交互结束,清理对象
            _interactivePopTransition = nil;
        }
    }

     

    第二个代理方法,要求在操作开始时候,返回一个实现UIViewControllerAnimatedTransitioning协议的对象,该对象实现了动画内容和执行时间。

    该协议有三个方法:

    // This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
    // synchronize with the main animation. 
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
    // This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
    
    
    @optional
    
    // This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.
    - (void)animationEnded:(BOOL) transitionCompleted;

    第一个方法返回动画时间;第二个方法,可以得到实现了UIViewControllerContextTransitioning协议的对象;第三个方法可选,如果transitionContext对象调用了completeTransition:方法,该代理方法会被调用。

    重点是第二个方法的transitionContext对象,其重要的方法如下:

    // The view in which the animated transition should take place.
    - (nullable UIView *)containerView;//动画所在的容器view
    - (BOOL)transitionWasCancelled;//转换是否取消// This must be called whenever a transition completes (or is cancelled.)
    // Typically this is called by the object conforming to the
    // UIViewControllerAnimatedTransitioning protocol that was vended by the transitioning
    // delegate.  For purely interactive transitions it should be called by the
    // interaction controller. This method effectively updates internal view
    // controller state at the end of the transition.
    - (void)completeTransition:(BOOL)didComplete;//完成转换,必须在动画完成时调用
    
    
    // Currently only two keys are defined by the
    // system - UITransitionContextToViewControllerKey, and
    // UITransitionContextFromViewControllerKey. 
    // Animators should not directly manipulate a view controller's views and should
    // use viewForKey: to get views instead.
    - (nullable __kindof UIViewController *)viewControllerForKey:(NSString *)key;//取得视图控制器
    
    // Currently only two keys are defined by the system -
    // UITransitionContextFromViewKey, and UITransitionContextToViewKey
    // viewForKey: may return nil which would indicate that the animator should not
    // manipulate the associated view controller's view.
    - (nullable __kindof UIView *)viewForKey:(NSString *)key NS_AVAILABLE_IOS(8_0);//取得视图控制器的view,兼容iOS7,可以直接用上面取到的viewController.view

    创建一个实现UIViewControllerAnimatedTransitioning协议的对象PopAnimation,重要代码如下:

    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
        return _transitionDuration;
    }
    
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
        _transitionContext = transitionContext;
    
        //当前控制器
        UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        
        //动画结束显示的控制器
        UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        
        //执行交互动画的容器view
        UIView *containerView = [transitionContext containerView];
        [containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
        
        NSTimeInterval duration = [self transitionDuration:transitionContext];
        
        switch (_popAnimationType) {
            case PopAnimationTranslation: {
                CGFloat containerWidth = containerView.frame.size.width;
                toViewController.view.transform = CGAffineTransformMakeTranslation(-containerWidth / 4.0, 0);
    
                [UIView animateWithDuration:duration animations:^{
                    toViewController.view.transform = CGAffineTransformMakeTranslation(0, 0);
                    fromViewController.view.transform = CGAffineTransformMakeTranslation(containerWidth, 0);
                }completion:^(BOOL finished) {
                    //动画结束,必须调用此方法
                    [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
                }];
    
                break;
            }
            case PopAnimationFlip: {
                [UIView beginAnimations:@"View Flip" context:nil];
                [UIView setAnimationDuration:duration];
                [UIView setAnimationDelegate:self];
                [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:containerView cache:YES];
                [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];
                [UIView commitAnimations];
                [containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
    
                break;
            }
            case PopAnimationCube: {
                CATransition *transiton = [CATransition animation];
                transiton.type = @"cube";
                transiton.subtype = @"fromLeft";
                transiton.duration = duration;
                transiton.removedOnCompletion = NO;
                transiton.fillMode = kCAFillModeForwards;
                transiton.delegate = self;
                [containerView.layer addAnimation:transiton forKey:nil];
                [containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
    
                break;
            }
        }
    }
    
    - (void)animationDidStop:(CATransition *)anim finished:(BOOL)flag {
        [_transitionContext completeTransition:!_transitionContext.transitionWasCancelled];
    }

    剩下的工作:

    在上一篇的基础上,修改UINavigationController的base_pushViewController方法

    - (void)base_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
    {
        if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.base_panGestureRecognizer]) {
            [self.interactivePopGestureRecognizer.view addGestureRecognizer:self.base_panGestureRecognizer];
            
            self.base_panGestureRecognizer.delegate = [self base_panGestureRecognizerDelegateObject];
            
            self.base_currentDelegateObject = [[NavigationControllerDelegateObject alloc] initWithUINavigationController:self];
            
            [self.base_panGestureRecognizer addTarget:self.base_currentDelegateObject action:@selector(handlePopGestureOperation:)];
            
            self.interactivePopGestureRecognizer.enabled = NO;
        }
        
        [self base_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController];
        
        if (![self.viewControllers containsObject:viewController]) {
            [self base_pushViewController:viewController animated:animated];
        }
    }

    可以看到,去除了私有变量和方法的调用,自定义了名为NavigationControllerDelegateObject的对象,其实现了UINavigationControllerDelegate协议,并实现了pan手势的处理方法。在此,我暴露了该属性对象,考虑到以后可能会增加或者修改UINavigationControllerDelegate的实现协议,可以为该对象增加分类来达到目的;还有一个目的,便于修改popAnimation的动画类型和执行时间。

     

    总结:

    两篇记录,从不同的方向,记录了右滑返回手势功能的实现。利用系统变量和方法,方便快捷;利用协议方法,高度自定义。

    但是,利用协议方法,需要处理的方面比较多。虽然上述已经记录了实现核心,示例代码中也有详细代码,但是仍然还有很多方面未考虑。

    目前,我发现的一些问题:

    1.导航栏交互效果也需要自行实现,暂未实现原生效果

    2.导航栏由隐藏到显示的效果,有时会出现异常

    3.如果带有bottomBar,效果不理想

     

    如果有好的解决办法,希望告知,谢谢。

     

    base项目已更新:git@github.com:ALongWay/base.git
    项目增加了新的UINavigationController+PopOperation分类,但是未引用到项目中,需要的同学,可先删除PopGesture分类的引用,再添加该分类的应用。

  • 相关阅读:
    HDU 1358 Period (KMP)
    POJ 1042 Gone Fishing
    Csharp,Javascript 获取显示器的大小的几种方式
    css text 自动换行的实现方法 Internet Explorer,Firefox,Opera,Safar
    Dynamic Fonts动态设置字体大小存入Cookie
    CSS Image Rollovers翻转效果Image Sprites图片精灵
    CSS three column layout
    css 自定义字体 Internet Explorer,Firefox,Opera,Safari
    颜色选择器 Color Picker,Internet Explorer,Firefox,Opera,Safar
    CSS TextShadow in Safari, Opera, Firefox and more
  • 原文地址:https://www.cnblogs.com/ALongWay/p/5896982.html
Copyright © 2011-2022 走看看