无障碍 关怀版

iOS Navigation Bar 导航栏折腾记(Swift&OC)

作为 iOS 开发者,难免要和导航栏打交道,通常呢,像微信这样优秀且友好的应用,全局使用系统导航栏交互效果就非常好了。然而为了更进一步,总是需要更深入地定制化导航栏,包括却不止像(半)透明、滑动渐变等交互效果,以及标题、颜色、偏移还有对应状态栏(StatusBar)的变化。

阅读参考了诸多开发者的经验分享,并发起了一个腾讯投票以了解大多数人是倾向于采用什么样的方式来处理导航栏的问题,于是决定采用系统导航栏+自定义导航栏共用的方式来处理。

发起的iOS导航栏自定义实现方式偏好投票

开始折腾之前,先简单说下我对这三种方式的理解。

修改系统导航栏

以添加Catagory(OC)或Extension(Swift)、重载系统方法等形式,拿到并修改系统导航栏的View,或添加所需要的View来实现自己定制化的需求。

优点

实现好后,各控制器定制起来调用方便,往往一两行代码就可以了。

能够保留侧滑返回的导航栏过渡效果(这个依需求而定,也并完全算优点)

缺点

实现方式复杂,涉及系统属性方法的修改,容易遇上各种未知的坑

这种方式可参考这几篇中文分享,写得非常详细:

  • 超简单!!! iOS设置状态栏、导航栏按钮、标题、颜色、透明度,偏移等【Swift】

  • iOS透明导航栏的平滑过渡(进阶版)【Obejective-C】

  • 让我们一次性解决导航栏的所有问题【Objective-C】

超简单!!! iOS设置状态栏、导航栏按钮、标题、颜色、透明度,偏移等【Swift】

iOS透明导航栏的平滑过渡(进阶版)【Obejective-C】

让我们一次性解决导航栏的所有问题【Objective-C】

完全使用自定义导航栏

隐藏系统导航栏,各页面采用自定义导航栏进行需求定制。

优点

避免系统导航栏存在的各种未知坑

实现效果可高度自定义,高兴的话可以设计成波浪形,还带动画交互的那种

一般有些应用采用底部导航栏的设计,基本都是完全使用自定义导航栏实现

缺点

一般没有系统导航栏的侧滑过渡效果,可参考手淘。(不算完全意义上的缺点)

依据不同的需求和实现方式,工作量可能较大

侧滑返回手势、滑动隐藏、触控隐藏等一些系统交互需自行实现

需要额外处理系统导航栏能够自动处理的in call等系统响应

系统导航栏与自定义导航栏共用

一般来说,一个优秀且友好的应用,多会遵循苹果官方的设计规范,故而绝大多数页面还是能够方便地采用系统导航栏进行处理,此时,部分页面出彩的交互设计,则可以暂时隐藏系统导航栏,采用自定义导航栏进行实现。

优点

避免修改系统导航栏可能遇到的坑

仅部分页面针对性采用自定义导航栏,工作量相对可控

采用系统导航栏的页面之间保留侧滑过渡效果

缺点

若是需要自定义导航栏的页面较多,工作增量较大

自定义导航栏页面的侧滑返回等效果需要额外处理

小结

总的来说,三种方式各有优缺,主要还是按照不同的需求采用不同的方案,若是导航栏真的需要水波烂漫的交互效果,侧滑返回的时候还要有个小船划回去,这若非要挑战通过修改系统导航栏的方式实现,费劲踩坑估计在所难免。

开始折腾 - [系统导航栏+自定义导航栏方案]

添加自定义导航栏

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)

}

}

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代理方法中对自定义导航栏的属性进行修改。

需要额外强调的是,最好在BaseViewController中对系统导航栏的一些属性做统一初始化处理,以期所有的控制器达到期望的统一效果,以避免自定义页面对系统导航栏的隐藏等修改影响到其它页面的系统导航栏。

override func viewWillAppear(_ animated: Bool) {

super.viewWillAppear(animated)

guard let navigationController = navigationController else {

return

}

// 仅处理导航栏隐藏后重新显示,可在此做更多导航栏的统一效果处理

if navigationController.isNavigationBarHidden {

navigationController.setNavigationBarHidden(false, animated: animated)

}

}

override func viewWillAppear(_ animated: Bool) {

super.viewWillAppear(animated)

guard let navigationController = navigationController else {

return

}

// 仅处理导航栏隐藏后重新显示,可在此做更多导航栏的统一效果处理

if navigationController.isNavigationBarHidden {

navigationController.setNavigationBarHidden(false, animated: animated)

}

}

处理StatusBar状态栏样式

override var preferredStatusBarStyle : UIStatusBarStyle {

return UIStatusBarStyle.lightContent

}

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)

}

}

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项目。若是希望继续了解修改系统导航栏的实现方式,可参考文中所提及的几篇分享,强烈推荐。

所以你偏好哪种方式呢?

  • 链接:https://juejin.im/post/599c4a89518825244d203dba

  • 程序员大咖整理发布,转载请联系作者获得授权

链接:https://juejin.im/post/599c4a89518825244d203dba

程序员大咖整理发布,转载请联系作者获得授权 返回搜狐,查看更多

责任编辑:

平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。
阅读 ()
推荐阅读

两个鬼故事东莞证券财富通宝宝起名大全宝典免费幸福在哪里电视剧网上家具城走进初中饰品店铺起名什么安全卫士好用专家起姓名大全奇皇后电视剧南方航空机票我的拿手好戏作文张姓大全起名大全孩子起名避讳几代长辈网游之剧毒公司起名字 测名鹦鹉起名字叫什么名字好听程氏女孩子起名皓然起名的含义宋 起名大全宗门起名梦见河流龙凤胎起名字宁姓女孩起什么名字好731遗址为什么不敢挖如何给高科技公司起名潘氏起名女孩名子鼠年范姓女孩起名起名广字辈男名字女孩起名带恒字美眉校探少年生前被连续抽血16次?多部门介入两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”淀粉肠小王子日销售额涨超10倍高中生被打伤下体休学 邯郸通报单亲妈妈陷入热恋 14岁儿子报警何赛飞追着代拍打雅江山火三名扑火人员牺牲系谣言张家界的山上“长”满了韩国人?男孩8年未见母亲被告知被遗忘中国拥有亿元资产的家庭达13.3万户19岁小伙救下5人后溺亡 多方发声315晚会后胖东来又人满为患了张立群任西安交通大学校长“重生之我在北大当嫡校长”男子被猫抓伤后确诊“猫抓病”测试车高速逃费 小米:已补缴周杰伦一审败诉网易网友洛杉矶偶遇贾玲今日春分倪萍分享减重40斤方法七年后宇文玥被薅头发捞上岸许家印被限制高消费萧美琴窜访捷克 外交部回应联合利华开始重组专访95后高颜值猪保姆胖东来员工每周单休无小长假男子被流浪猫绊倒 投喂者赔24万小米汽车超级工厂正式揭幕黑马情侣提车了西双版纳热带植物园回应蜉蝣大爆发当地回应沈阳致3死车祸车主疑毒驾恒大被罚41.75亿到底怎么缴妈妈回应孩子在校撞护栏坠楼外国人感慨凌晨的中国很安全杨倩无缘巴黎奥运校方回应护栏损坏小学生课间坠楼房客欠租失踪 房东直发愁专家建议不必谈骨泥色变王树国卸任西安交大校长 师生送别手机成瘾是影响睡眠质量重要因素国产伟哥去年销售近13亿阿根廷将发行1万与2万面值的纸币兔狲“狲大娘”因病死亡遭遇山火的松茸之乡“开封王婆”爆火:促成四五十对奥巴马现身唐宁街 黑色着装引猜测考生莫言也上北大硕士复试名单了德国打算提及普京时仅用姓名天水麻辣烫把捣辣椒大爷累坏了

两个鬼故事 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化