RDVTabBarController 是一個(gè)定制化的TabBarController庫(kù),可動(dòng)畫(huà)顯示隱藏tabbar欄,可定制tabbar欄,代碼庫(kù)地址在這箱靴,效果如下:
用法與UITarbarController差不多,一開(kāi)始需要設(shè)置它的ViewControllers
RDVTabBarController *tabBarController = [[RDVTabBarController alloc] init];
[tabBarController setViewControllers:@[firstNavigationController, secondNavigationController, thirdNavigationController]];
具體的請(qǐng)看github上的介紹荷愕。
接下來(lái)看看衡怀,TabBar欄是怎么被添加到TarbarController的view上,為什么它能在每個(gè)ViewController上都能顯示的安疗。
首先抛杨,在RDVTabBarController類(lèi)的viewDidLoad方法看到:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:[self contentView]];
[self.view addSubview:[self tabBar]];
}
注意添加順序,先添加contentView荐类,后添加tabBar怖现,這樣tabBar欄就不會(huì)被contentView覆蓋擋住了。此時(shí)view的視圖層級(jí)是這樣的:
只要設(shè)置一下它們的大小和位置玉罐,也就能變成這樣:
接著看viewWillAppear方法:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self setSelectedIndex:[self selectedIndex]];
[self setTabBarHidden:self.isTabBarHidden animated:NO];
}
先看里面的setSelectedIndex方法:
- (void)setSelectedIndex:(NSUInteger)selectedIndex {
if (selectedIndex >= self.viewControllers.count) {
return;
}
//第一步
if ([self selectedViewController]) {
[[self selectedViewController] willMoveToParentViewController:nil]; //(1)
[[[self selectedViewController] view] removeFromSuperview]; //(2)
[[self selectedViewController] removeFromParentViewController]; //(3)
}
_selectedIndex = selectedIndex;
[[self tabBar] setSelectedItem:[[self tabBar] items][selectedIndex]];
//第二步
[self setSelectedViewController:[[self viewControllers] objectAtIndex:selectedIndex]];
[self addChildViewController:[self selectedViewController]];(1)
[[[self selectedViewController] view] setFrame:[[self contentView] bounds]];
[[self contentView] addSubview:[[self selectedViewController] view]];(2)
[[self selectedViewController] didMoveToParentViewController:self];(3)
[self setNeedsStatusBarAppearanceUpdate];
}
重點(diǎn)看屈嗤,第一步,移除上一個(gè)所選的ViewController:
(1)willMoveToParentViewController方法吊输,傳nil參數(shù)表示子視圖控制器將被移除饶号;
(2)視圖控制器的view從父視圖中移除;
(3)子視圖控制器從父視圖控制器中移除季蚂,會(huì)自動(dòng)調(diào)用didMoveToParentViewController方法來(lái)通知回調(diào)茫船。
第二步,添加所選的ViewController:
(1)添加視圖控制器扭屁,會(huì)自動(dòng)調(diào)用willMoveToParentViewController方法算谈;
(2)添加視圖控制器的view;
(3)完成添加視圖控制器料滥,
要留意的是然眼,是將ViewController的View添加到contentView中,而不是TabBarController的view幔欧。而以后push視圖控制器的話,都是將視圖控制器的view添加到contentView中丽声,這樣tabBar就始終能覆蓋在contentView之上顯示出來(lái)礁蔗。
再看RDVTabBarController類(lèi)中的setTabBarHidden方法:
- (void)setTabBarHidden:(BOOL)hidden animated:(BOOL)animated {
_tabBarHidden = hidden;
__weak RDVTabBarController *weakSelf = self;
//動(dòng)畫(huà)中執(zhí)行的block
void (^block)() = ^{
CGSize viewSize = weakSelf.view.bounds.size;
CGFloat tabBarStartingY = viewSize.height;
CGFloat contentViewHeight = viewSize.height;
CGFloat tabBarHeight = CGRectGetHeight([[weakSelf tabBar] frame]);
if (!tabBarHeight) {
tabBarHeight = 49;//tabBar默認(rèn)高度
}
if (!hidden) {
tabBarStartingY = viewSize.height - tabBarHeight;
if (![[weakSelf tabBar] isTranslucent]) {
contentViewHeight -= ([[weakSelf tabBar] minimumContentHeight] ?: tabBarHeight);
}
[[weakSelf tabBar] setHidden:NO];
}
[[weakSelf tabBar] setFrame:CGRectMake(0, tabBarStartingY, viewSize.width, tabBarHeight)];
[[weakSelf contentView] setFrame:CGRectMake(0, 0, viewSize.width, contentViewHeight)];
};
//動(dòng)畫(huà)完成的block
void (^completion)(BOOL) = ^(BOOL finished){
if (hidden) {
[[weakSelf tabBar] setHidden:YES];
}
};
if (animated) {
[UIView animateWithDuration:0.24 animations:block completion:completion]; //執(zhí)行動(dòng)畫(huà)
} else {
block();
completion(YES);
}
}
這個(gè)方法是設(shè)置TabBar欄顯示或隱藏,所使用的動(dòng)畫(huà)比較簡(jiǎn)單雁社,動(dòng)畫(huà)中的block主要設(shè)置contentView和tabBar欄的大小和位置浴井。而tabBar欄其實(shí)就是一個(gè)普通的view,通過(guò)設(shè)置它的hidden屬性霉撵,就能讓tabBar欄顯示或隱藏磺浙。
其實(shí)洪囤,通過(guò)分析RDVTabBarController源碼,也就明白如何自定義一個(gè)容器視圖控制器了撕氧,同時(shí)也能大概明白UITabBarController內(nèi)部的組織結(jié)構(gòu)了瘤缩。
最后,請(qǐng)看蘋(píng)果官方文檔:自定義視圖控制器容器伦泥!