最近接到一个非常规需求,正常来说一个UITabBarController最多只会有五个Item,如果有第六个会采用侧滑或者其他的方案。当然如果你要执意系统的UITabBarController,系统会自动的将第五个以及之后的都合并成UITabBarController上的第五个UIBarButton,变成一个More
然后第五个以及之后的item都在这个More界面的里面UItabView中,如下图:
是不是会特别的丑。那么如果需求非要展示六个甚至更多呢?可以采用想安卓一样的做法(PS:我们公司的安卓告诉我是这个样子的,不知道是不是真的这样哈),左右可以滑动的。
首先我们看下UITabBarConroller的图层结构
可以看出第一层的交互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