本项目用于实现 UINavigationBar 背景渐变过渡动画。
可能会遇到的问题
UINavigationBar 上的一个系统 bug (苹果的 UINavigationBar bug)
bug 描述:导航右滑返回手势,概率性地导致返回后页面的 rightBarButtonItem 的 tintColor 颜色变浅,bug 现象如下:
bug 代码:
- 在 viewDidLoad 中设置 rightBarButtonItem 会导致 bug 产生。bug 是概率性发生的,不易复现。
override func viewDidLoad() { super.viewDidLoad() // 在 viewDidLoad 中设置 rightBarButtonItem self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(title: "Next", style: .plain, target: self, action: #selector(pushNextViewController)) self.view.backgroundColor = UIColor.white self.title = "Title" + " " + "\(self.page)" } @objc public func pushNextViewController() { let vc = self.nextViewController; vc.page = self.page + 1 self.navigationController?.pushViewController(vc, animated: true) }
bug 解决:
- 方式2:在 viewWillAppear 中设置 rightBarButtonItem。
override func viewDidLoad() { super.viewDidLoad() // 将 rightBarButtonItem 设置移至 viewWillAppear // self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(title: "Next", style: .plain, target: self, action: #selector(pushNextViewController)) self.view.backgroundColor = UIColor.white self.title = "Title" + " " + "\(self.page)" } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 在 viewWillAppear 中设置 rightBarButtonItem self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(title: "Next", style: .plain, target: self, action: #selector(pushNextViewController)) } @objc public func pushNextViewController() { let vc = self.nextViewController; vc.page = self.page + 1 self.navigationController?.pushViewController(vc, animated: true) }
- 方法2:issues12
bug 工程源码:UINavigationBarBug
效果
介绍
NNNavigationBar 是一个用于实现导航条背景渐变过渡动画的轻量级代码库。
实现
- 代码库通过 Category/Method Swizzling 方式 hook UINavigationBar 的方法调用,实现导航条背景渐变过渡动画。
轻量
- 仅对 UINavigationBar 进行了 Method Swizzling 方法混淆。不涉及其它类的方法混淆,如 UIViewController、UINavigationController 等。
- 仅对 UINavigationBar/UINavigationItem 进行了必要的属性关联。
原理
UINavigationItem (category_xxx)
|
|
V
①
add [.nn_xxx]
| UIViewController
| -------------------> [.navigationItem]
|
|
V
②
set vcn.navigationItem.nn_xxx
|
|
|
UINavigationController V
vc stack ③
| vcn | <----- navigationController push/pop vcn
| ... | |
| vc1 | |
| vc0 | |
|
UINavigationBar |
----------------------- |
| <—— title | |
----------------------- |
| |
| UINavigationBar.Items V
| ④-② item stack ④-①
|<--- update Bar --- | vcn.navigationItem | <--- navigationBar push/pop vcn.navigationItem
| | | ... | |
| | | vc2.navigationItem | |
| | | vc1.navigationItem | |
| | | vc0.navigationItem | |
| | |
| | ④-③ |
| |---------------------> hook <-----------------------|
| |
| |
| |
| ⑤ |
|<--- update Bar [.nn_xx] -----------|
- 在 UINavigationItem 的 Category 中使用 runtime 添加属性 [.nn_xx]。
- 每个 UIViewController 都有一个 UINavigationItem 属性 navigationItem,在 UIViewController 中修改 navigationItem 对象的属性 [.nn_xx]。
- 在 UINavigationController push/pop UIViewController 时,会将 UIViewController 的 navigationItem 对象 push/pop 给 UINavigationBar。
- 通过 Method Swizzling 的方式 hook UINavigationBar 方法调用,获取对应方法的调用时机。
- 在合适的时刻,UINavigationBar 从 navigationItem 对象中获取属性 [.nn_xx],更新 UINavigationBar 的状态(本代码库实现了背景的平滑渐变过渡)。
使用
- 导入头文件
#import "NNNavigationBar.h"
- 颜色渐变过渡
- (void)viewDidLoad {
[super viewDidLoad];
// 去除系统背景
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
// 显示自定义背景
self.navigationController.navigationBar.nn_backgroundViewHidden = false;
// 设置背景颜色
self.navigationItem.nn_backgroundColor = [UIColor orangeColor];
}
- 图片渐变过渡
- (void)viewDidLoad {
[super viewDidLoad];
// 去除系统背景
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[UIImage new]];
// 显示自定义背景
self.navigationController.navigationBar.nn_backgroundViewHidden = false;
// 设置背景图片
self.navigationItem.nn_backgroundImage = [UIImage imageNamed:xx_image];
}
- 更多使用,详见 demo
安装
通过 CocoaPods 集成
安装最新版的 CocoaPods:
$ gem install cocoapods
在 podfile
中添加:
pod 'NNNavigationBar', '~> 2.7.3'
然后在终端执行:
$ pod install
如安装失败,提示:
[!] Unable to find a specification for `NNNavigationBar`
尝试使用命令:
pod install --repo-update
通过 Carthage 集成
Carthage 是一个去中心化的依赖管理器,用于构建依赖和提供二进制 Framework。
可以通过以下 Homebrew 命令安装 Carthage:
$ brew update
$ brew install carthage
通过 Carthage 将 NNNavigationBar 集成到 Xcode 项目中,需要在 Cartfile
中添加:
github "amisare/NNNavigationBar" ~> 2.7.3
执行 carthage
构建 Framework,并将 NNNavigationBar.framework
添加到 Xcode 项目中。
系统要求
- iOS 8.0+
鸣谢
- Logo 由 anharismail 设计
许可证
NNNavigationBar 是基于 MIT 许可协议发布的,详情请见 LICENSE 文件。