美文网首页iOS复制粘贴ios
iOS-可交互滑动的TabBarController

iOS-可交互滑动的TabBarController

作者: cdcyd | 来源:发表于2016-07-29 11:30 被阅读2664次

    1.先看一下效果


    左右滑动交互的TabBarController

    2.在iOS7.0以前,要实现这样的效果,只有自定义TabBar了,但这很麻烦。而在iOS7.0以后,苹果在UITabBarControllerDelegate中增加了下面两个代理方法:

    /** 
     *  实现该代理,即可以实现自定义的各界面切换时的动画(如平推,缩放,淡入淡出等)
     *  fromVC:当前显示的VC
     *  toVC:将要切换到的VC
     *  返回一个自定义的切换动画,在本例中,我自定义了一个平推效果的动画
     */
    - (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
                animationControllerForTransitionFromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);
    
    /**
     *  实现该代理,即可以实现与动画的交互
     *  tabBarController:当前的tabBarController
     *  animationController:动画百分比控制器
     *  返回一个自定义的动画百分比控制器,以控制当前动画进行的百分比。
     */
    - (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
                          interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController NS_AVAILABLE_IOS(7_0);
    

    3.下面是实现过程

    • 首先需要一个TabBarController,在本例中,我写了两个MainViewController(点击item切换时,也用自定义动画)、MainTabBarViewController(点击item是没有动画),可以在AppDelegate中选则使用哪一个。
    • 在TabBarController中,添加一个pan手势
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.delegate = self;
        self.selectedIndex = 0;
    
        // 添加pan手势
        _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognizer:)];
        [self.view addGestureRecognizer:self.panGestureRecognizer];
    
        // 添加TabBar子控制器
        壹ViewController *vc1 = [[壹ViewController alloc]init];
        贰ViewController *vc2 = [[贰ViewController alloc]init];
        叁ViewController *vc3 = [[叁ViewController alloc]init];
        肆ViewController *vc4 = [[肆ViewController alloc]init];
        伍ViewController *vc5 = [[伍ViewController alloc]init];
        self.viewControllers = @[vc1,vc2,vc3,vc4,vc5];
    
        for (int i = 0; i < self.tabBar.items.count; i ++) {
            NSDictionary *dic = @{NSForegroundColorAttributeName: [UIColor colorWithRed:0.451 green:0.553 blue:0.584 alpha:1.00]};
            NSDictionary *selecteddic = @{NSForegroundColorAttributeName: [UIColor colorWithRed:0.384 green:0.808 blue:0.663 alpha:1.00]};
    
            UITabBarItem *item = [self.tabBar.items objectAtIndex:i];
            [item setTitleTextAttributes:dic forState:UIControlStateNormal];
            [item setTitleTextAttributes:selecteddic forState:UIControlStateSelected];
            
            switch (i) {
                case 0:
                    item.title = @"壹";
                    break;
                case 1:
                    item.title = @"贰";
                    break;
                case 2:
                    item.title = @"叁";
                    break;
                case 3:
                    item.title = @"肆";
                    break;
                case 4:
                    item.title = @"伍";
                    break;
                default:
                    break;
            }
        }
    }
    
    • pan手势的实现
    - (void)panGestureRecognizer:(UIPanGestureRecognizer *)pan{
        // 如果没有动画控制器,则不执行动画,动画控制器就是用来展示切换动画
        if (self.transitionCoordinator) {
            return;
        }
        
        // 开始执行动画
        if (pan.state == UIGestureRecognizerStateBegan || pan.state == UIGestureRecognizerStateChanged){
            [self beginInteractiveTransitionIfPossible:pan];
        }
    }
    
    - (void)beginInteractiveTransitionIfPossible:(UIPanGestureRecognizer *)sender{
        // 通过滑动的方法判断应该像那边跳转
        CGPoint translation = [sender translationInView:self.view];
        if (translation.x > 0.f && self.selectedIndex > 0) {
            self.selectedIndex --;
        } 
        else if (translation.x < 0.f && self.selectedIndex + 1 < self.viewControllers.count) {
            self.selectedIndex ++;
        } 
        else {
            if (!CGPointEqualToPoint(translation, CGPointZero)) {
                sender.enabled = NO;
                sender.enabled = YES;
            }
        }
        
        // 告知动画控制器,开始执行动画,这里需要注意:苹果提供了两个方法,但是我们只有选择这个方法,并且只有这样写才能按我们的预期执行,否则会有BUG,这一点我也不知道原因
        [self.transitionCoordinator animateAlongsideTransitionInView:self.view animation:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            
        } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            if ([context isCancelled] && sender.state == UIGestureRecognizerStateChanged){
                [self beginInteractiveTransitionIfPossible:sender];
            }
        }];
    }
    
    • 代理的实现
    - (id<UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
        // 判断是否是通过手势
        if (self.panGestureRecognizer.state == UIGestureRecognizerStateBegan || self.panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
            // 判断跳转的方向
            NSArray *viewControllers = tabBarController.viewControllers;
            if ([viewControllers indexOfObject:toVC] > [viewControllers indexOfObject:fromVC]) {
                // 返回的自定义动画
                return [[TabBarTransitionAnimation alloc] initWithTargetEdge:UIRectEdgeLeft];
            } 
            else {
                return [[TabBarTransitionAnimation alloc] initWithTargetEdge:UIRectEdgeRight];
            }
        }
        else{
            // 返回空,表示用系统的动画
            return nil;
        }
    }
    
    - (id<UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController{
        if (self.panGestureRecognizer.state == UIGestureRecognizerStateBegan || self.panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
            // 返回自定义的动画百分比控制器
            return [[TabBarTransitionController alloc] initWithGestureRecognizer:self.panGestureRecognizer];
        } 
        else {
            // 返回空,表示用系统的
            return nil;
        }
    }
    
    • 自定义平推的动画,自定义动画需要遵守<UIViewControllerAnimatedTransitioning>协议,需要实现下面方法
    // 返回动画执行时间
    - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
        return 0.35;
    }
    
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
        // 获取执行动画的ViewController
        UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
        UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
        
        // 获取执行动画的View,注意:该方式只有iOS8.0才有,如果要适配iOS8.0以前,请用fromViewController.view获取
        UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
        
        // 获取执行动画前,两个view的初始frame
        CGRect fromFrame = [transitionContext initialFrameForViewController:fromViewController];
        CGRect toFrame = [transitionContext finalFrameForViewController:toViewController];
       
        // 执行动画前frame的偏移向量
        CGVector offset;
        if (self.targetEdge == UIRectEdgeLeft){
            offset = CGVectorMake(-1.f, 0.f);
        }
        else if (self.targetEdge == UIRectEdgeRight){
            offset = CGVectorMake(1.f, 0.f);
        }
        else{
            NSAssert(NO, @"targetEdge must be one of UIRectEdgeLeft, or UIRectEdgeRight.");
        }
            
        fromView.frame = fromFrame;
        toView.frame = CGRectOffset(toFrame, 
                                    toFrame.size.width * offset.dx * -1,
                                    toFrame.size.height * offset.dy * -1);
        
        // 将toView添加到动画控制器中
        [transitionContext.containerView addSubview:toView];
        NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
        // 开始执行动画
        [UIView animateWithDuration:transitionDuration animations:^{
            fromView.frame = CGRectOffset(fromFrame, 
                                          fromFrame.size.width * offset.dx,
                                          fromFrame.size.height * offset.dy);
            toView.frame = toFrame;
        } completion:^(BOOL finished) {
            // 动画执行完成
            [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        }];
    }
    
    • 自定义动画百分比控制器,这个苹果一个给我们定义好了一个类,我们只需要继承UIPercentDrivenInteractiveTransition,就可以轻松实现,该类主要有以下方法
    // 动画时间
    @property (readonly) CGFloat duration;
    // 动画完成的百分比
    @property (readonly) CGFloat percentComplete;
    // 完成的速度
    @property (nonatomic,assign) CGFloat completionSpeed;
    // 动画的加速度 
    @property (nonatomic,assign) UIViewAnimationCurve completionCurve;
    
    // 通过该方法来告知当前动画完成的百分比
    - (void)updateInteractiveTransition:(CGFloat)percentComplete;
    // 取消动画
    - (void)cancelInteractiveTransition;
    // 完成动画
    - (void)finishInteractiveTransition;
    
    // 本例中的实现
    - (CGFloat)percentForGesture:(UIPanGestureRecognizer *)gesture{
        // 通过手势在屏幕中滑动的距离来判断当前执行的百分比
        UIView *transitionContainerView = self.transitionContext.containerView;
        CGPoint translation = [gesture translationInView:gesture.view.superview];
        if ((translation.x > 0.f && self.initialTranslationInContainerView.x < 0.f) ||
            (translation.x < 0.f && self.initialTranslationInContainerView.x > 0.f)){
            return -1.f;
        }
        return fabs(translation.x)/CGRectGetWidth(transitionContainerView.bounds);
    }
    
    // 这个手势是TabBar中传递过来的
    - (void)gestureRecognizeDidUpdate:(UIScreenEdgePanGestureRecognizer *)gestureRecognizer{
        switch (gestureRecognizer.state) {
            case UIGestureRecognizerStateBegan:
                break;
            case UIGestureRecognizerStateChanged:
                if ([self percentForGesture:gestureRecognizer] < 0.f) {
                    [self cancelInteractiveTransition];
                    [self.gestureRecognizer removeTarget:self action:@selector(gestureRecognizeDidUpdate:)];
                } 
                else {
                    // 更新当前动画进度
                    [self updateInteractiveTransition:[self percentForGesture:gestureRecognizer]];
                }
                break;
            case UIGestureRecognizerStateEnded:
                if ([self percentForGesture:gestureRecognizer] >= 0.4f){
                    // 结束手势,动画执行大于40%时,完成动画
                    [self finishInteractiveTransition];
                }
                else{
                    // 否则取消动画
                    [self cancelInteractiveTransition];
                }
                break;
            default:
                [self cancelInteractiveTransition];
                break;
        }
    }
    

    4.最后附上Demo的下载地址:https://github.com/cdcyd/CommonControlsCollection

    相关文章

      网友评论

      • 芝士就是力量_42a2:不能改变title
      • 走停2015_iOS开发:哥们 我发现iPhone6手势切换tabbar 在一瞬间出现了一个白色的界面 不知道是什么原因
      • 春风依旧:公司让按照安卓做的,正好有需要
        小北风sky:WXTabBarController Github上有,我没记错的话
      • 727eacd06b43:TabBarmei没有图片 背景应该是白色的
      • 大牛之路:我找到你demo中MainTabbarVC为什么没有转场动画了,你在实现平滑动画的时候添加了手势判断。你把那个手势判断去掉。因为你点击item的时候是没有滑动手势的,所以那个方法不会进

      本文标题:iOS-可交互滑动的TabBarController

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