關於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的左右滑動切換的問題算是解決了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM