在我们开发的过程中,有时会有左右侧滑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的左右滑动切换的问题算是解决了。