关于tabbarViewController的左右滑动切换

Posted 竹峰拾贝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于tabbarViewController的左右滑动切换相关的知识,希望对你有一定的参考价值。

在我们开发的过程中,有时会有左右侧滑tabbarViewController切换控制器的需求,在我自己做项目的时候遇到了此类需求,现在就在此记录一下我当时的做法,废话不多说,直接上代码:

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

- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController  interactionControllerForAnimationController: (id<UIViewControllerAnimatedTransitioning>)animationController;

- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController  animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC;

自定义的滑动代理,在此实现tabbarViewController的两个新添加的滑动的代理方法

//  ScrollTabBarDelegate.h

 

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

 

@interface ScrollTabBarDelegate : NSObject <UITabBarControllerDelegate>

 

@property (nonatomic, assign) BOOL interactive;

@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactionController;

 

@end

//

//  ScrollTabBarDelegate.m

#import "ScrollTabBarDelegate.h"

#import "ScrollTabBarAnimator.h"

 

@interface ScrollTabBarDelegate ()

 

@property (nonatomic, strong) ScrollTabBarAnimator *tabBarAnimator;

 

@end

 

@implementation ScrollTabBarDelegate

 

- (instancetype)init {

    if (self = [super init]) {

        _interactive = NO;

        _interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];

        _tabBarAnimator = [[ScrollTabBarAnimator alloc] init];

 

    }

    return self;

}

 

- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController

                               interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController {

    return self.interactive ? self.interactionController : nil;

}

 

- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController

                     animationControllerForTransitionFromViewController:(UIViewController *)fromVC

                                                       toViewController:(UIViewController *)toVC {

    

    NSInteger fromIndex = [tabBarController.viewControllers indexOfObject:fromVC];

    NSInteger toIndex = [tabBarController.viewControllers indexOfObject:toVC];

    self.tabBarAnimator.tabScrollDirection = (toIndex < fromIndex) ? TabLeftDirection: TabRightDirection;

    return self.tabBarAnimator;

 

}

 

@end

在自定义一个动画的类,实现滑动切换tabbar时加载动画效果:

//  ScrollTabBarAnimator.h

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

 

typedef NS_ENUM(NSInteger,TabOperationDirection) {

    TabLeftDirection,

    TabRightDirection

};

 

@interface ScrollTabBarAnimator : NSObject <UIViewControllerAnimatedTransitioning>

 

@property (nonatomic, assign) TabOperationDirection tabScrollDirection;

 

@end

//  ScrollTabBarAnimator.m

#import "ScrollTabBarAnimator.h"

 

@implementation ScrollTabBarAnimator

 

//动画持续时间

- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {

    return 0.3;

}

 

//动画执行效果

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {

    

    // 获取 toView fromView

    UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

    UIView *containerView = [transitionContext containerView];

    if (!toViewController || !fromViewController || !containerView) return;

    

    // 给 toView fromView 设定相应的值

    toViewController.view.transform = CGAffineTransformIdentity;

    fromViewController.view.transform = CGAffineTransformIdentity;

    CGFloat translation = containerView.frame.size.width;

 

    switch (self.tabScrollDirection) {

        case TabLeftDirection:

            translation = translation;

            break;

        case TabRightDirection:

            translation = -translation;

            break;

        default:

            break;

    }

 

    [containerView addSubview:toViewController.view];

    toViewController.view.transform = CGAffineTransformMakeTranslation(-translation, 0);

    // 真正的变化

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{

        fromViewController.view.transform = CGAffineTransformMakeTranslation(translation, 0);

        toViewController.view.transform = CGAffineTransformIdentity;

    } completion:^(BOOL finished) {

        fromViewController.view.transform = CGAffineTransformIdentity;

        toViewController.view.transform = CGAffineTransformIdentity;

        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];

    }];

}

 

@end

在 TabBarController.m 中 实现的方法

导入 ScrollTabBarDelegate 的头文件后,声明两个对象

@property (nonatomic, assign) NSInteger subViewControllerCount;

@property (nonatomic, strong) ScrollTabBarDelegate *tabBarDelegate;

- (void)viewDidLoad {

    [super viewDidLoad];

    #pragma mark   tabbar滑动切换手势

    // 正确的给予 count

    self.subViewControllerCount = self.viewControllers ? self.viewControllers.count : 0;

设置 tabbarViewController 的代理为 ScrollTabBarDelegate ,并给 tabbarViewController 的view添加滑动手势

    // 代理

    self.tabBarDelegate = [[ScrollTabBarDelegate alloc] init];

    self.delegate = self.tabBarDelegate;

    // 增加滑动手势

    self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandle:)];

    [self.view addGestureRecognizer:self.panGesture];

    

}

 实现滑动手势,切换tabbarViewController的item到不同的控制器

- (void)panHandle:(UIPanGestureRecognizer *)panGesture {

    // 获取滑动点

    CGFloat translationX = [panGesture translationInView:self.view].x;

    CGFloat progress = fabs(translationX)/self.view.frame.size.width;

    

    switch (panGesture.state) {

        case UIGestureRecognizerStateBegan:

        {

            self.tabBarDelegate.interactive = YES;

            CGFloat velocityX = [panGesture velocityInView:self.view].x;

            if (velocityX < 0) {

                if (self.selectedIndex < self.subViewControllerCount - 1) {

                    self.selectedIndex += 1;

                }

            }

            else {

                if (self.selectedIndex > 0) {

                    self.selectedIndex -= 1;

                }

            }

        }

            break;

        case UIGestureRecognizerStateChanged:

        {

            [self.tabBarDelegate.interactionController updateInteractiveTransition:progress];

        }

            

            break;

        case UIGestureRecognizerStateEnded:

        case UIGestureRecognizerStateFailed:

        case UIGestureRecognizerStateCancelled:

        {

            if (progress > 0.3) {

                self.tabBarDelegate.interactionController.completionSpeed = 0.99;

                [self.tabBarDelegate.interactionController finishInteractiveTransition];

            }else{

                //转场取消后,UITabBarController 自动恢复了 selectedIndex 的值,不需要我们手动恢复。

                self.tabBarDelegate.interactionController.completionSpeed = 0.99;

                [self.tabBarDelegate.interactionController cancelInteractiveTransition];

            }

            self.tabBarDelegate.interactive = NO;

        }

            break;

        default:

            break;

    }

}

这样实现以后,会出现一个问题,就是当跳转到子控制器的时候,子控制器也可以滑动,这就不对了,于是果断的开始查找原因,发现所有的控制器的 

rootViewController 都是 tabbarViewController,所以需要在跳转的时候吧 tabbarViewController 的view的滑动手势给移除掉,当返回来的时候再给加上去,于是把 UIPanGestureRecognizer 的对象在tabbarViewController.h 中搞成一个全局对象 

@property (nonatomic, strong) UIPanGestureRecognizer *panGesture;

在自定义的 UINavigationController 中,重写 pushViewController 方法,在此方法中移除tabbarViewController 的view的滑动手势,代码如下: 

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {

    

    if (self.viewControllers.count > 0) {

        viewController.hidesBottomBarWhenPushed = YES;

        // 跳转后移除滑动手势

        [[UIApplication sharedApplication].delegate.window.rootViewController.view removeGestureRecognizer:viewController.cyl_tabBarController.panGesture];

   注册一个通知,通知在第一层的控制器吧tabbarViewController 的view的滑动手势给添加上去

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollNextViewController) name:@"SCROLLTABBARCONTROLLER" object:nil];

 

    }

        [super pushViewController:viewController animated:animated];

}

当返回到第一层的控制器的时候,在返回按钮点击的时候发布一个通知,通知在第一层的控制器吧tabbarViewController 的view的滑动手势给添加上去

-(void)scrollNextViewController {

    

    if (self.childViewControllers.count == 1 && [(AppDelegate *)[UIApplication sharedApplication].delegate isBackBtn] == YES) {

        // 返回后重新添加滑动手势

        [[UIApplication sharedApplication].delegate.window.rootViewController.view addGestureRecognizer:[UIApplication sharedApplication].delegate.window.rootViewController.cyl_tabBarController.panGesture];

    }

}

结束后别忘了移除通知

- (void)dealloc {

    [[NSNotificationCenter defaultCenter] removeObserver:self];

}

到此处,关于tabbarViewController的左右滑动切换的问题算是解决了。

以上是关于关于tabbarViewController的左右滑动切换的主要内容,如果未能解决你的问题,请参考以下文章

iOS 将 TabbarViewController 放入 NavigationController 但 navigationBar 为 nil

iPhone:如何在我的 TabBarViewController 和各个选项卡之间共享信息?

如何创建以 TabBarViewController 为根的导航堆栈?

复习知识点:TabBarViewController(微信框架)

使用不是 rootViewController 的 TabBarViewController 更改 UITabBar 的图标

TabBarViewController 的导航栏覆盖来自导航控制器的导航栏