解决 iOS 导航栏隐藏和显示

今天在项目中遇到了隐藏 navigationbar 功能的问题,例如:从 A push到 B 页面,A 页面的 navigationbar 是隐藏的,但是 B 页面的 navigationbar 是需要显示的。

一开始我在 A 页面调用 setNavigationBarHidden(true, animated: true) 方法,在 B 页面调用 setNavigationBarHidden(false, animated: true) 方法,虽然能够达到想要的效果,但是对于项目来说是灾难性的。因为如果有多个地方出现这种情况的话,你的代码将会变得十分的杂乱臃肿。

一种好的解决方式是调用 navigationcontroller 的代理:

1
navigationController(_:willShow:animated:)

将这个代理放在项目控制器的基类中,在这里我是放在我的基类 BaseViewController 中。为什么这么做?因为这样可以很好的去掉冗余的代码,不用写的到处都是,我的所有控制器都是继承自 BaseViewController 的,BaseViewController 继承自 UIViewController

下面到了 Show Code 的时候了

基类实现代理方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
extension BaseViewController: UINavigationControllerDelegate {

public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {

/// 这里判断是否是当前代理控制器,如果是当前代理控制器的话,则是需要隐藏 navigationbar 的
if viewController == self {
navigationController.setNavigationBarHidden(true, animated: true)
} else {
/// 系统相册不能隐藏,所有就直接 return
if navigationController.isKind(of: UIImagePickerController.self) {
return
}
/// 不是当前代理控制器的话,显示真正的 navbar
navigationController.setNavigationBarHidden(false, animated: true)
/// 当不显示本页时,要么就 push 到下一页,要么就被 pop 了,那么就将 delegate 设置为 nil,防止出现 BAD ACCESS
/// 之前将这段代码放在 viewDidDisappear 和 dealloc 中,这两种情况可能已经被 pop 了,self.navigationController 为 nil,这里采用手动持有 navigationController 的引用来解决
if let delegate = navigationController.delegate, delegate === self {
/// 如果 delegate 是自己才设置为 nil,因为 viewWillAppear 调用的比此方法较早,
/// 其他 controller 如果设置了 delegate 就可能会被误伤
navigationController.delegate = nil
}
}
}
}

在需要隐藏 navigationbar 的地方,添加如下代码

1
self.navigationController?.delegate = self

这样就能解决显示/隐藏的问题了。

参考链接