在我們?nèi)粘i_發(fā)中酿愧,總有那么一兩個界面需要去隱藏導航欄沥潭,這時如何去合理的處理呢?筆者這里提供了幾個常用的方案和帶來的問題嬉挡,并在最后給個筆者認為較為優(yōu)雅的方法钝鸽。
方案一:使用setNavigationBarHidden:animated:
方法直接處理
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:true animated:animated];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:false animated:animated];
}
這個使我們解決隱藏導航欄首先會想到的方案汇恤,這種方式雖然很好的解決了,首頁隱藏導航欄拔恰,push
到新界面不隱藏的場景因谎。但如下場景會有個過度動畫,很難看:
- 首頁需要隱藏颜懊,
push
的新界面也需要隱藏财岔,這時就會有個隱藏--顯示--隱藏的過度動畫;
- 首頁需要隱藏颜懊,
- 首頁隱藏河爹,然后在切換
tabBar
再回來匠璧,這時有一個導航欄向上消失的動畫;
- 首頁隱藏河爹,然后在切換
所以這種直接使用的方案咸这,不完美夷恍,pass。
方案二:使用UINavigationControllerDelegate
代理方法直接處理
@interface HomePageController () <UINavigationControllerDelegate>
@end
@implementation HomePageController
#pragma mark - lifeCycle
- (void)viewDidLoad {
[super viewDidLoad];
// 設(shè)置導航控制器的代理為self
self.navigationController.delegate = self;
}
#pragma mark - < UINavigationControllerDelegate >
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// 判斷要顯示的控制器是否是自己
BOOL isShowHomePage = [viewController isKindOfClass:[self class]];
[self.navigationController setNavigationBarHidden:isShowHomePage animated:YES];
}
- (void)dealloc {
self.navigationController.delegate = nil;
}
通過當前self
對象來管理導航欄的顯示和隱藏媳维,雖然能解決切換tabBar
的動畫問題酿雪,但是還是沒有解決上面的問題1。
不完美侄刽,pass指黎。
方案三:參考FDFullscreenPopGesture
思路的實現(xiàn)方案,完美解決以上問題
主要的思路是使用runtime
去hook
UIViewController
的viewWillAppear:
方法和navigationController
的pushViewController:animated:
及setViewControllers:animated:
方法實現(xiàn)州丹。
相當于是在每個UIViewController
控制器袋励,在調(diào)用viewWillAppear:
方法的時候,都去判斷下是否需要隱藏導航欄setNavigationBarHidden:animated:
方法当叭,這樣的好處是茬故,不用再當前控制器的viewWillDisappear:
中寫顯示方法,也就沒有了過渡的動畫問題蚁鳖,完美解決磺芭。
- 給
UIViewController
添加一個設(shè)置隱藏導航欄的屬性lsl_prefersNavigationBarHidden
:
// MARK: - 給UIViewController添加lsl_prefersNavigationBarHidden屬性
@implementation UIViewController (HandlerNavigationBar)
- (BOOL)lsl_prefersNavigationBarHidden
{
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
- (void)setLsl_prefersNavigationBarHidden:(BOOL)hidden
{
objc_setAssociatedObject(self, @selector(lsl_prefersNavigationBarHidden), @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
-
hook
UIViewController
的viewWillAppear:
方法,并在此方法中執(zhí)行已經(jīng)存好的代碼塊:
typedef void(^_LSLViewControllerWillAppearInjectBlock)(UIViewController *viewController, BOOL animated);
@interface UIViewController (HandlerNavigationBarPrivate)
@property(nonatomic, copy) _LSLViewControllerWillAppearInjectBlock lsl_willAppearInjectBlock;
@end
// MARK: - 替換UIViewController的viewWillAppear方法醉箕,在此方法中钾腺,執(zhí)行設(shè)置導航欄隱藏和顯示的代碼塊。
@implementation UIViewController (HandlerNavigationBarPrivate)
+ (void)load
{
Method orginalMethod = class_getInstanceMethod(self, @selector(viewWillAppear:));
Method swizzledMethod = class_getInstanceMethod(self, @selector(lsl_viewWillAppear:));
method_exchangeImplementations(orginalMethod, swizzledMethod);
}
- (void)lsl_viewWillAppear:(BOOL)animated
{
[self lsl_viewWillAppear:animated];
if (self.lsl_willAppearInjectBlock) {
self.lsl_willAppearInjectBlock(self, animated);
}
}
- (_LSLViewControllerWillAppearInjectBlock)lsl_willAppearInjectBlock
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setLsl_willAppearInjectBlock:(_LSLViewControllerWillAppearInjectBlock)block
{
objc_setAssociatedObject(self, @selector(lsl_willAppearInjectBlock), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
-
hook
住navigationController
的pushViewController:animated:
及setViewControllers:animated:
方法讥裤,當控制器被壓入棧中的時候放棒,預存設(shè)置隱藏和顯示導航欄的代碼塊到即將顯示的控制器中,備控制器調(diào)用:
// MARK: - 替換UINavigationController的pushViewController:animated:方法己英,在此方法中去設(shè)置導航欄的隱藏和顯示
@implementation UINavigationController (NavigationBar)
+ (void)load
{
Method originMethod = class_getInstanceMethod(self, @selector(pushViewController:animated:));
Method swizzedMethod = class_getInstanceMethod(self, @selector(lsl_pushViewController:animated:));
method_exchangeImplementations(originMethod, swizzedMethod);
Method originSetViewControllersMethod = class_getInstanceMethod(self, @selector(setViewControllers:animated:));
Method swizzedSetViewControllersMethod = class_getInstanceMethod(self, @selector(lsl_setViewControllers:animated:));
method_exchangeImplementations(originSetViewControllersMethod, swizzedSetViewControllersMethod);
}
- (void)lsl_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// Handle perferred navigation bar appearance.
[self lsl_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController];
// Forward to primary implementation.
[self lsl_pushViewController:viewController animated:animated];
}
- (void)lsl_setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated
{
// Handle perferred navigation bar appearance.
for (UIViewController *viewController in viewControllers) {
[self lsl_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController];
}
// Forward to primary implementation.
[self lsl_setViewControllers:viewControllers animated:animated];
}
- (void)lsl_setupViewControllerBasedNavigationBarAppearanceIfNeeded:(UIViewController *)appearingViewController
{
if (!self.lsl_viewControllerBasedNavigationBarAppearanceEnabled) {
return;
}
// 即將被調(diào)用的代碼塊
__weak typeof(self) weakSelf = self;
_LSLViewControllerWillAppearInjectBlock block = ^(UIViewController *viewController, BOOL animated){
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
[strongSelf setNavigationBarHidden:viewController.lsl_prefersNavigationBarHidden animated:animated];
}
};
// 給即將顯示的控制器间螟,注入代碼塊
appearingViewController.lsl_willAppearInjectBlock = block;
// 因為不是所有的都是通過push的方式,把控制器壓入stack中,也可能是"-setViewControllers:"的方式厢破,所以需要對棧頂控制器做下判斷并賦值荣瑟。
UIViewController *disappearingViewController = self.viewControllers.lastObject;
if (disappearingViewController && !disappearingViewController.lsl_willAppearInjectBlock) {
disappearingViewController.lsl_willAppearInjectBlock = block;
}
}
- (BOOL)lsl_viewControllerBasedNavigationBarAppearanceEnabled
{
NSNumber *number = objc_getAssociatedObject(self, _cmd);
if (number) {
return number.boolValue;
}
self.lsl_viewControllerBasedNavigationBarAppearanceEnabled = YES;
return YES;
}
- (void)setLsl_viewControllerBasedNavigationBarAppearanceEnabled:(BOOL)enabled
{
SEL key = @selector(lsl_viewControllerBasedNavigationBarAppearanceEnabled);
objc_setAssociatedObject(self, key, @(enabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
方案三可以完美解決以上所遇見的問題,demo
中有相應的案例和完整代碼摩泪。