導航欄顯示和隱藏的坑
在iOS開發中,經常需要從一個無NavigationBar的控制器push到一個有NavigationBar的控制器,或者相反。看似只要設定一下NavigationBar的Hidden屬性就可以了,其實裡面還有不少坑。
隱藏導航欄的方法很簡單,只要在控制器將要出現的時候設定NavigationBar隱藏就可以了,然後在控制器將要消失的時候重新顯示NavigationBar,效果如圖1所示。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// 隱藏導航欄方法1
self .navigationController.navigationBarHidden = YES;
// 方法2
// [self.navigationController setNavigationBarHidden:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO];
}
圖1.gif
但是仔細觀察會發現切換的過程並不順滑:1.有導航欄的控制器出現時,導航欄會立即出現,而控制器的View是自右向左漸入的(簡書其實就是這樣的);2.點選返回按鈕時,導航欄消失且右側會出現黑邊。如圖2所示。
圖2.gif
因為在push頁面的時候,animated屬性是設定成YES的,所以控制器View的出現會有動畫。animated屬性通常都是設定成YES的,這樣的頁面切換會讓人比較舒服。
[self.navigationController pushViewController:[[HQThirdViewController alloc] init] animated:YES];
所以我們猜想一下,導航欄的顯示和隱藏是不是也應該有個animated屬性。果不其然,設定導航欄隱藏還有另一個方法可以開啟和關閉動畫,我們開啟動畫之後再看看效果,如圖3。
- (void)viewWillAppear: (BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
}
圖3.gif
導航欄的顯示和控制器的View顯示都有動畫了,pop的時候也不會出現黑邊了。這個animated屬性官方是這樣解釋的:If animated, it will transition vertically using UINavigationControllerHideShowBarDuration. 意思就是說如果開啟動畫,導航欄會以某個時長進行垂直過渡。
對於UINavigationControllerHideShowBarDuration官方文件也給出瞭解釋:This variable specifies the duration when animating the navigation bar. Note that this is a constant value, so it cannot be set. 就是說UINavigationControllerHideShowBarDuration決定了導航欄動畫的時長,注意這是一個常量,不能被改變。
這樣就完美解決了嗎?不,另一個坑出現了。點選TabBarItem進入"我的"頁面的時候,導航欄也出現了動畫,因為動畫只能寫在ViewWillAppear方法裡,所以每次顯示頁面都會呼叫。
圖4.gif
現在這種情況下,animated屬性肯定是不能開啟的,但是pop時候的黑邊問題又該怎麼解決?
解決方法1
首先想想為什麼pop的時候導航欄直接就消失了,因為專案中我把導航欄的translucent屬性關閉了(這個屬性預設是開啟的),控制器的View不會有穿透效果,而pop的時候導航欄隱藏又沒有開啟動畫效果,所以就導致了導航欄直接消失。那麼我們再來看看開啟translucent屬性的效果,如圖5。
圖5.gif
黑邊不會再出現了,導航欄依舊是立即消失,但是控制器的View填充了整個畫面。這是一種解決方法,大家可以看看釘釘iOS客戶端,從設定頁面pop回我的頁面也是這種效果。
最後,如果希望Pop的時候導航欄不會立即消失而且沒有黑邊,切換TabBarItem的時候又不會出現動畫,那麼依舊還是要開啟animated屬性的。
解決方法2
1.給"我的"控制器.h檔案裡新增一個關閉動畫的屬性
@interface HQMineViewController : UITableViewController
@property (nonatomic, assign) BOOL closeAnimating;
@end
2.在自定義的TabBarController裡面實現UITabBarControllerDelegate,並實現如下方法
@interface HQTabBarController ()<UITabBarControllerDelegate>
@end
@implementation HQTabBarController
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
UINavigationController *navigationController = (UINavigationController *)viewController;
if ([navigationController.topViewController isKindOfClass:[HQMineViewController class]])
{
HQMineViewController *mineVc = (HQMineViewController *)navigationController.topViewController;
// 點選TabBarItem進入"我的"控制器 會關閉導航欄消失的動畫
mineVc.closeAnimating = YES;
}
return YES;
}
}
3.修改"我的"控制器中隱藏導航欄的方法
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:!self.closeAnimating];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO animated:YES];
// 控制器消失時要開啟動畫,保證由其他方式進入控制器會有動畫
self.closeAnimating = NO;
}
4.最終效果如圖6所示
圖6.gif
解決方法3
走了這麼多的彎路,接下來就放出最終解決方法了,其實只要將animated屬性繼承ViewWillAppear(Disappear)的animated屬性即可,恍然大悟。
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
總結
方法3最簡單,又能完美解決NavigationBar顯示和隱藏切換的問題,順便簡單地實現了tableHeaderView的下拉放大。