做为 iOS 开发者,不免要和导航栏打交道,一般呢,像微信这样优秀且友好的应用,全局使用系统导航栏交互效果就很是好了。然而为了更进一步,老是须要更深刻地定制化导航栏,包括却不止像(半)透明
、滑动渐变
等交互效果,以及标题
、颜色
、偏移
还有对应状态栏(StatusBar)的变化。ios
阅读参考了诸多开发者的经验分享,并发起了一个腾讯投票以了解大多数人是倾向于采用什么样的方式来处理导航栏的问题,因而决定采用系统导航栏+自定义导航栏
共用的方式来处理。swift
开始折腾以前,先简单说下我对这三种方式的理解。微信
以添加Catagory(OC)
或Extension(Swift)
、重载系统方法
等形式,拿到并修改系统导航栏的View,或添加所须要的View来实现本身定制化的需求。并发
这种方式可参考这几篇中文分享,写得很是详细:ide
隐藏系统导航栏,各页面采用自定义导航栏进行需求定制。动画
通常来讲,一个优秀且友好的应用,多会遵循苹果官方的设计规范,故而绝大多数页面仍是可以方便地采用系统导航栏进行处理,此时,部分页面出彩的交互设计,则能够暂时隐藏系统导航栏,采用自定义导航栏进行实现。ui
总的来讲,三种方式各有优缺,主要仍是按照不一样的需求采用不一样的方案,如果导航栏真的须要水波烂漫的交互效果,侧滑返回的时候还要有个小船划回去,这若非要挑战经过修改系统导航栏的方式实现,费劲踩坑估计在所不免。spa
fileprivate lazy var customNavigationItem: UINavigationItem = UINavigationItem(title: "Profile")
fileprivate lazy var customNavigationBar: UINavigationBar = {
let bar = UINavigationBar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 64))
bar.tintColor = UIColor.white
bar.tintAdjustmentMode = .normal
bar.alpha = 0
bar.setItems([self.customNavigationItem], animated: false)
bar.backgroundColor = UIColor.clear
bar.barStyle = UIBarStyle.blackTranslucent
bar.isTranslucent = true
bar.shadowImage = UIImage()
bar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
let textAttributes = [
NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName: UIFont.systemFont(ofSize: 16)
]
bar.titleTextAttributes = textAttributes
return bar
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customNavigationBar)
prepareData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: true)
// 便于自定义BarButtomItem
setBackButton()
customNavigationBar.alpha = 1.0
}
func setBackButton() {
let backBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "back_white"), style: .plain, target: self, action: #selector(DataProjectDetailViewController.back(_:)))
self.customNavigationItem.leftBarButtonItem = backBarButtonItem
}
@objc fileprivate func back(_ sender: AnyObject) {
if let presentingViewController = presentingViewController {
presentingViewController.dismiss(animated: true, completion: nil)
} else {
_ = navigationController?.popViewController(animated: true)
}
}复制代码
如果须要对导航栏进行滑动动画或渐变等处理,则在ScrollView代理方法中对自定义导航栏的属性进行修改。.net
须要额外强调的是,最好在BaseViewController
中对系统导航栏的一些属性作统一初始化处理,以期全部的控制器达到指望的统一效果,以免自定义页面对系统导航栏的隐藏等修改影响到其它页面的系统导航栏。设计
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let navigationController = navigationController else {
return
}
// 仅处理导航栏隐藏后从新显示,可在此作更多导航栏的统一效果处理
if navigationController.isNavigationBarHidden {
navigationController.setNavigationBarHidden(false, animated: animated)
}
}复制代码
override var preferredStatusBarStyle : UIStatusBarStyle {
return UIStatusBarStyle.lightContent
}复制代码
重点!敲黑板、敲黑板了。处理边缘侧滑返回,须要接管实现导航控制器的边缘侧滑返回交互手势代理。好在全部的导航控制器来继承了BaseNavigationController
,于是能够在基类进行统一处理。
class BaseNavigationController: UINavigationController {
override func setNavigationBarHidden(_ hidden: Bool, animated: Bool) {
super.setNavigationBarHidden(hidden, animated: animated)
// 接管导航控制器的边缘侧滑返回交互手势代理
interactivePopGestureRecognizer?.delegate = self
}
}
extension BaseNavigationController: UIGestureRecognizerDelegate {
// 让边缘侧滑手势在合适的状况下生效
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if (self.viewControllers.count > 1) {
return true;
}
return false;
}
// 容许同时响应多个手势
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
// 避免响应边缘侧滑返回手势时,当前控制器中的ScrollView跟着滑动
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return gestureRecognizer.isKind(of: UIScreenEdgePanGestureRecognizer.self)
}
}复制代码
这样就经过自定义添加方式实现了导航栏的定制化,其余页面则继续愉快使用系统导航栏便可。以上就是全部自定义导航栏须要的核心代码了,故没有另外的Demo项目。如果但愿继续了解修改系统导航栏的实现方式,可参考文中所说起的几篇分享,强烈推荐。