自定义可滑动的UITabBarController

    最近接到一个非常规需求,正常来说一个UITabBarController最多只会有五个Item,如果有第六个会采用侧滑或者其他的方案。当然如果你要执意系统的UITabBarController,系统会自动的将第五个以及之后的都合并成UITabBarController上的第五个UIBarButton,变成一个More

自定义可滑动的UITabBarController_第1张图片

然后第五个以及之后的item都在这个More界面的里面UItabView中,如下图:

自定义可滑动的UITabBarController_第2张图片

是不是会特别的丑。那么如果需求非要展示六个甚至更多呢?可以采用想安卓一样的做法(PS:我们公司的安卓告诉我是这个样子的,不知道是不是真的这样哈),左右可以滑动的。

具体实现思路:

        首先我们看下UITabBarConroller的图层结构

自定义可滑动的UITabBarController_第3张图片

可以看出第一层的交互view是UITabBar,然后每个可以点击按钮是UITabBarButton。那么要滑动我们就需要让TabBar可以滑动。

把UITabBar上的每个UITabBarButton给隐藏。一开始的思路是把UITabBarController的tabbar给隐藏的,但是发现在11的系统上隐藏后UITabBarButton任然可以看见。所以就换个思路隐藏tabBar的子视图UITabBarButton,代码如下:

  for (UIView *subview in self.tabBar.subviews) {
            if ([[NSString stringWithUTF8String:object_getClassName(subview)] isEqualToString:@"UITabBarButton"]) {
                subview.hidden = YES;
            }
        }

通过遍历tabBar的subview,找到class类型是UITabBarButton,并把他隐藏调。这个UItabButton系统并没有开放给我们,如果开发了我们可以自己创建UITabBarButton,那么后面的操作也会简单很多。因此我们只能自己通过自定义view来实现了,并给他实现好交互事件。

@interface SSXTabBarButton : UIView
@property (nonatomic, strong) UITabBarItem *tabBarItem;
@property (nonatomic, strong) UIImage *norImage;
@property (nonatomic, strong) UIImage *selectedImage;
@property (nonatomic, assign) NSInteger badgeValue;
@property (nonatomic, assign) BOOL isSelected;
@property (nonatomic, assign) NSInteger index;
@end

自定义view的属性大致如此集体实现我就不说了,在文章结尾我会贴上项目源码。

2.将自定义的类似UItabBarButton的view给添加到scrollview上

 _tabBarArr = [[NSMutableArray alloc] initWithCapacity:self.viewControllers.count];
        for (i=0; i=4) {//如果不设置左边的tabbarButton会出现more的返回按钮
                [nav.viewControllers firstObject].navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] init];
            }
            SSXTabBarButton *tabBarButton = [[SSXTabBarButton alloc] initWithFrame:CGRectMake(SSX_SCREEN_WIDTH/5*i, tabBarButtonFrame.origin.y, (SSX_SCREEN_WIDTH -2*6)/5, tabBarButtonFrame.size.height)];
            tabBarButton.tabBarItem = nav.tabBarItem;
            [_contentView addSubview:tabBarButton];
            tabBarButton.index = i;
            [nav.tabBarItem addObserver:self forKeyPath:@"badgeValue" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
            UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTabBarSelectItem:)];
            [tabBarButton addGestureRecognizer:tap];
            [_tabBarArr addObject:tabBarButton];
        }
        [self.tabBar addSubview:_contentView];
        self.selectedIndex = 0;
}

并给每个item的nav上的tabBarItem的BadgeValue属性添加一个KVO,来同步我们自定义view上的角标同步变化,重点来了,这里会有一个坑,当uitabbar的uitabbarbutton的数量超过5个时,从第五个起所有的当前类的viewcontronller的nav都会变成moreNavigationController。也就是说上图的 "关注、我的"界面通过self.navigationController获取到的nav都是moreNavigationController,moreNavigationController的tabBarItem不是单独和关注、我的模块单独绑定的,所以无法做到角标同步,因此这里我做了一个比较low的处理方式。就是index>=4的模块改角标的时候用自定义tabBar提供的方法

/**
 设置tabbar上的角标未读
 当tabBar的Item数量大于5时,index >= 4的地方 对应的位置的nav是tabBar的moreNavigationController所以设置     self.navigationController.tabBarItem.badgeValue设置未读的方法失效,因此要通过下列方法设置
 @param badgeValue 未读内容
 @param tabBarIndex 对应的tabr的index
 
 */
- (void)setSSXTabBarItemBadgeValue:(NSString *)badgeValue ForTabBarIndex:(NSInteger)tabBarIndex;

当然这里我也层尝试干掉moreNavigationController的方式,或者更改TabrBar的viewControllers。即时是通过KVC去修改也是无效的。当index<4的界面,也就是上图中的首页、消息、联系人、工作台这几个界面仍然可以通过修改角标仍然可以通过self.navigationController.tabBarItem来进行修改badgeValue。但是像关注、我的这个两个界面修改角标只能通过自定义方法来解决了。(如果你们看过我的源码后有不通过自定义同步后面两个界面角标的方式请联系我,谢谢哈,当时这个问题纠结我两天了,最后我还是妥协加入口修改了,没能够实现自定义方法实现同步)。

代理方法我只保留了两个常用的

- (void)setSelectedIndex:(NSUInteger)selectedIndex{
    if (_ssxTabBarStyle == SSXTabBarStyleScroll) {
        UIViewController *didSelectViewController = (UIViewController *)self.viewControllers[selectedIndex];
        if ([self.delegate respondsToSelector:@selector(tabBarController:shouldSelectViewController:)]) {
            if (![self.delegate tabBarController:self shouldSelectViewController:didSelectViewController]) {
                return;
            }
        }
        [super setSelectedIndex:selectedIndex];
        if ([self.delegate respondsToSelector:@selector(tabBarController:didSelectViewController:)]) {
            [self.delegate tabBarController:self didSelectViewController:didSelectViewController];
        }
        SSXTabBarButton *tabBarBtn = _tabBarArr[selectedIndex];
        tabBarBtn.isSelected = YES;
        if (tabBarBtn==selectedTabBtn) {
            return;
        }else{
            selectedTabBtn.isSelected = NO;
            selectedTabBtn = tabBarBtn;
        }
    }else{
        [super setSelectedIndex:selectedIndex];
    }
    
}

如果是不超过五个控制器的用这个自定义tababr会完全全部走原来的系统方法。


源码地址:https://github.com/XiuSheng/SSXTabBar

也可以 pod search SSXTabBar

使用方法直接tabBar.viewControllers 赋值即可,会自动识别是否是需要滑动

UITabBar我获取到相关属性方法协议:(希望对我上述提到的同步问题有帮助,我之前尝试过改写系统的一些方法觉没有办法干掉moreNavigationController     )

  084520  property---->pu_isTabBarHidden
  084708  property---->px_tabBarHidden
  084896  property---->_backdropGroupName
  085204  property---->rememberedFocusedItemsByViewController
  085325  property---->moreChildViewControllers
  085690  property---->_animator
  085837  property---->_interactor
  086212  property---->_accessoryView
  086656  property---->viewControllers
  086790  property---->selectedViewController
  087003  property---->selectedIndex
  087118  property---->moreNavigationController
  087360  property---->customizableViewControllers
  087610  property---->tabBar
  087849  property---->delegate
  088242  property---->hash
  088424  property---->superclass
  088548  property---->description
  088666  property---->debugDescription
  088790  method---->setNewMoreChildViewControllers:
  089124  method---->pu_isTabBarHidden
  089384  method---->px_diagnosticsItemProvidersForPoint:inCoordinateSpace:
  089634  method---->px_frameForTabItem:inCoordinateSpace:
  089800  method---->px_navigateToMemoryWithLocalIdentifier:
  089915  method---->px_canPerformAddToTabAnimationForTab:
  090096  method---->px_isTabBarHidden
  090336  method---->px_performAddToTabAnimation:withSnapshotView:
  090621  method---->.cxx_destruct
  090732  method---->initWithCoder:
  090841  method---->encodeWithCoder:
  090953  method---->setDelegate:
  091058  method---->dealloc
  091170  method---->traitCollectionDidChange:
  091289  method---->delegate
  091396  method---->gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
  091640  method---->supportedInterfaceOrientations
  092225  method---->pressesBegan:withEvent:
  092911  method---->pressesEnded:withEvent:
  093241  method---->pressesCancelled:withEvent:
  093639  method---->_deepestUnambiguousResponder
  093791  method---->becomeFirstResponder
  093875  method---->encodeRestorableStateWithCoder:
  093958  method---->decodeRestorableStateWithCoder:
  094509  method---->setView:
  094672  method---->pressesChanged:withEvent:
  094757  method---->loadView
  094924  method---->initWithNibName:bundle:
  095049  method---->shouldAutorotateToInterfaceOrientation:
  095261  method---->_willChangeToIdiom:onScreen:
  095357  method---->_setAnimator:
  095748  method---->_animator
  095843  method---->preferredFocusedView
  096136  method---->preferredFocusEnvironments
  096230  method---->_subclassPreferredFocusedViewPrioritizationType
  096420  method---->setRestorationIdentifier:
  096563  method---->_responderSelectionContainerViewForResponder:
  096755  method---->_didUpdateFocusInContext:withAnimationCoordinator:
  096847  method---->_overridingDestinationEnvironmentForFocusUpdateInContext:
  096933  method---->animationDidStop:finished:context:
  097015  method---->_transitionView
  097102  method---->shouldUpdateFocusInContext:
  097186  method---->focusedViewDidChange
  097267  method---->viewWillAppear:
  097347  method---->viewDidAppear:
  097426  method---->viewWillDisappear:
  097505  method---->viewDidDisappear:
  097875  method---->_gestureRecognizerShouldBegin:
  097957  method---->_accessoryView
  098051  method---->transitionViewDidStart:
  098132  method---->transitionViewDidComplete:fromView:toView:
  098214  method---->_setInteractor:
  098295  method---->_interactor
  098379  method---->_updateLayoutForStatusBarAndInterfaceOrientation
  098461  method---->sizeForChildContentContainer:withParentContainerSize:
  098544  method---->willTransitionToTraitCollection:withTransitionCoordinator:
  098628  method---->_performBackGesture:
  098709  method---->_edgeInsetsForChildViewController:insetsAreAbsolute:
  099049  method---->preferredInterfaceOrientationForPresentation
  099170  method---->_isPresentationContextByDefault
  099354  method---->_shouldPersistViewWhenCoding
  099557  method---->viewDidLoad
  099755  method---->_populateArchivedChildViewControllers:
  099944  method---->_backdropBarGroupName
  100198  method---->_viewsWithDisabledInteractionGivenTransitionContext:
  100388  method---->purgeMemoryForReason:
  100656  method---->_reallyWantsFullScreenLayout
  100913  method---->_isSupportedInterfaceOrientation:
  101075  method---->transitionCoordinator
  101186  method---->willRotateToInterfaceOrientation:duration:
  101500  method---->willAnimateRotationToInterfaceOrientation:duration:
  101594  method---->didRotateFromInterfaceOrientation:
  101679  method---->_allowsAutorotation
  101763  method---->_shouldSynthesizeSupportedOrientations
  101843  method---->_hasPreferredInterfaceOrientationForPresentation
  101927  method---->rotatingHeaderView
  102009  method---->rotatingFooterView
  102093  method---->_shouldUseOnePartRotation
  102182  method---->willAnimateFirstHalfOfRotationToInterfaceOrientation:duration:
  102722  method---->willAnimateSecondHalfOfRotationFromInterfaceOrientation:duration:
  102910  method---->_getRotationContentSettings:
  103253  method---->didAnimateFirstHalfOfRotationToInterfaceOrientation:
  103473  method---->childViewControllerForStatusBarStyle
  103580  method---->childViewControllerForStatusBarHidden
  103667  method---->childViewControllerForWhitePointAdaptivityStyle
  103751  method---->unwindForSegue:towardsViewController:
  103832  method---->_transitionsChildViewControllers
  103912  method---->rotatingSnapshotViewForWindow:
  103993  method---->_rememberPresentingFocusedItem:
  104173  method---->updateTabBarItemForViewController:
  104257  method---->_allContainedViewControllers
  104338  method---->_additionalViewControllersToCheckForUserActivity
  104419  method---->setViewControllers:animated:
  104499  method---->_frameForViewController:
  104579  method---->_frameForWrapperViewForViewController:
  104660  method---->_setBackdropGroupName:
  104740  method---->_backdropGroupName
  104820  method---->tabBar
  104900  method---->_rememberFocusedItem:forViewController:
  104980  method---->_recallRememberedFocusedItemForViewController:
  105114  method---->_forgetFocusedItemForViewController:
  105193  method---->_rememberedFocusedItemsByViewController
  105390  method---->viewControllers
  105527  method---->_layoutViewController:
  105637  method---->_effectiveTabBarPosition
  105852  method---->_selectedViewControllerInTabBar
  106092  method---->_hideBarWithTransition:isExplicit:
  106258  method---->showBarWithTransition:
  106339  method---->__viewWillLayoutSubviews
  106689  method---->setViewControllers:
  106899  method---->_effectiveMaxItems
  107108  method---->selectedViewController
  107316  method---->_viewForViewController:
  107496  method---->setSelectedViewController:
  107672  method---->_wrapperViewForViewController:
  107897  method---->_rebuildTabBarItemsIfNeeded
  108044  method---->_setSelectedTabBarItem:
  108260  method---->_layoutContainerView
  108344  method---->_selectDefaultViewControllerIfNecessaryWithAppearanceTransitions:
  108425  method---->_adjustTabBarFrameForOffscreenFocus:barPosition:
  108730  method---->_setAccessoryView:
  108884  method---->_prepareTabBar
  109129  method---->_setTabBarVisualAltitude
  109229  method---->_isBarHidden
  109333  method---->_updateGestureRecognizersForTraitCollection:
  109435  method---->_updateLayoutForTraitCollection:
  109520  method---->_rebuildTabBarItemsAnimated:
  109666  method---->_showBarWithTransition:isExplicit:
  109747  method---->_setTabBarPosition:
  109941  method---->_setViewControllers:animated:
  111031  method---->_performSelectGesture:
  111178  method---->_performLeftGesture:
  111268  method---->_performRightGesture:
  111348  method---->_performTouchDetectionGesture:
  111545  method---->_isTabBarFocused
  111819  method---->_performFocusGesture:
  112184  method---->_tabBarItemClicked:
  112393  method---->_configureTargetActionForTabBarItem:
  112477  method---->moreNavigationController
  112609  method---->transientViewController
  112886  method---->setTransientViewController:animated:
  112968  method---->selectedIndex
  113360  method---->_viewControllersInTabBar
  113677  method---->_existingMoreNavigationController
  113760  method---->_viewControllerForSelectAtIndex:
  113840  method---->setSelectedIndex:
  113921  method---->setCustomizableViewControllers:
  114002  method---->_allowSelectionWithinMoreList
  114083  method---->setTransientViewController:
  114163  method---->_setSelectedViewController:
  114243  method---->allViewControllers
  114323  method---->transitionFromViewController:toViewController:
  114405  method---->customizableViewControllers
  114485  method---->_setMoreNavigationControllerRestorationIdentifier
  115009  method---->_allowsCustomizing
  115213  method---->_viewControllerForTabBarItem:
  115312  method---->concealTabBarSelection
  115614  method---->transitionFromViewController:toViewController:transition:shouldSetSelected:
  115954  method---->_customAnimatorForFromViewController:toViewController:
  116139  method---->_customInteractionControllerForAnimator:
  116238  method---->revealTabBarSelection
  116319  method---->_doAllViewControllersSupportInterfaceOrientation:
  116399  method---->tabBar:willBeginCustomizingItems:
  116700  method---->tabBar:willEndCustomizingItems:changed:
  116968  method---->tabBar:didEndCustomizingItems:changed:
  117052  method---->_shouldAdjustContentViewFrameForOffscreenFocus:adjustedTabBarFrame:
  117400  method---->_adjustContentViewFrameForOffscreenFocus:viewController:
  117538  method---->setTabBar:
  117718  method---->_setUpFocusContainerGuides
  117951  method---->_updateOffscreenStatus:
  118192  method---->beginCustomizingTabBar:
  118373  method---->tabBarSizingDidChange:
  118497  method---->hideBarWithTransition:
  118644  method---->_tabBarPosition
  118981  method---->setShowsEditButtonOnLeft:
  119123  method---->showsEditButtonOnLeft
  119205  method---->_setMaximumNumberOfItems:
  119287  method---->_setSelectedViewControllerNeedsLayout
  119367  method---->_setBadgeValue:forTabBarItem:
  119447  method---->moreChildViewControllers
  119550  method---->setMoreChildViewControllers:
  119633  method---->_ignoreUnselectedTabsForStateRestoration
  119725  Ivar---->_tabBar
  119804  Ivar---->_containerView
  119889  Ivar---->_viewControllerTransitionView
  119973  Ivar---->_tabBarItemsToViewControllers
  121024  Ivar---->_selectedViewController
  121145  Ivar---->_moreNavigationController
  121450  Ivar---->_customizableViewControllers
  121615  Ivar---->_selectedViewControllerDuringWillAppear
  121793  Ivar---->_transientViewController
  122094  Ivar---->_customMaxItems
  122247  Ivar---->_defaultMaxItems
  122427  Ivar---->_tabBarPosition
  122696  Ivar---->_backGestureRecognizer
  122843  Ivar---->_nudgeLeftGestureRecognizer
  123038  Ivar---->_nudgeRightGestureRecognizer
  123124  Ivar---->_selectGestureRecognizer
  123329  Ivar---->_touchDetectionGestureRecognizer
  123415  Ivar---->_contentFocusContainerGuide
  123496  Ivar---->_tabBarControllerFlags
  123578  Ivar---->_moreChildViewControllers
  123656  Ivar---->_accessoryView
  123736  Ivar---->_rememberedFocusedItemsByViewController
  123819  Ivar---->_delegate
  123899  Ivar---->__backdropGroupName
  123977  Ivar---->__animator
  124057  Ivar---->__interactor
  124163  protocol---->UIGestureRecognizerDelegate
  124514  protocol---->UILayoutContainerViewDelegate
  124687  protocol---->UITabBarDelegate
  124768  protocol---->NSCoding

你可能感兴趣的:(自定义可滑动的UITabBarController)