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

2017 年 8 月 24 日 CocoaChina Binboy_王兴彬

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


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


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


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


修改系统导航栏


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


优点


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

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


缺点


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

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



完全使用自定义导航栏


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


优点


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

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

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


缺点


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

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

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

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


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


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


优点


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

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

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


缺点


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

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


小结


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



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


添加自定义导航栏


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)

        }

    }


处理StatusBar状态栏样式


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


所以你偏好哪种方式呢?


微信扫一扫,选择属于你的阵营吧!


博客原文链接


登录查看更多
1

相关内容

苹果公司在 WWDC 2014 开幕 Keynote 上发布的全新编程语言,具有更多现代化特性,同时容易使用,定位是补充 Objective-C. > Swift is an innovative new programming language for Cocoa and Cocoa Touch. Writing code is interactive and fun, the syntax is concise yet expressive, and apps run lightning-fast. Swift is ready for your next iOS and OS X project — or for addition into your current app — because Swift code works side-by-side with Objective-C.

Swift - Apple Developer

【2020新书】实战R语言4,323页pdf
专知会员服务
100+阅读 · 2020年7月1日
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
【实用书】Python爬虫Web抓取数据,第二版,306页pdf
专知会员服务
117+阅读 · 2020年5月10日
Python计算导论,560页pdf,Introduction to Computing Using Python
专知会员服务
73+阅读 · 2020年5月5日
【经典书】Python计算机视觉编程,中文版,363页pdf
专知会员服务
139+阅读 · 2020年2月16日
【干货】谷歌Joshua Gordon 《TensorFlow 2.0讲解》,63页PPT
专知会员服务
27+阅读 · 2019年11月2日
渗透某德棋牌游戏
黑白之道
12+阅读 · 2019年5月17日
7 款实用到哭的App,只说一遍
高效率工具搜罗
84+阅读 · 2019年4月30日
从webview到flutter:详解iOS中的Web开发
前端之巅
5+阅读 · 2019年3月24日
iOS自定义带动画效果的模态框
CocoaChina
7+阅读 · 2019年3月3日
可能是 Android 上最好用的写作 App
少数派
10+阅读 · 2018年12月21日
Android P正式发布,你需要尽快做适配了
前端之巅
3+阅读 · 2018年8月7日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
Arxiv
3+阅读 · 2018年10月8日
Large-Scale Study of Curiosity-Driven Learning
Arxiv
8+阅读 · 2018年8月13日
Arxiv
3+阅读 · 2018年5月28日
Arxiv
3+阅读 · 2018年3月28日
Arxiv
4+阅读 · 2018年3月14日
VIP会员
相关资讯
渗透某德棋牌游戏
黑白之道
12+阅读 · 2019年5月17日
7 款实用到哭的App,只说一遍
高效率工具搜罗
84+阅读 · 2019年4月30日
从webview到flutter:详解iOS中的Web开发
前端之巅
5+阅读 · 2019年3月24日
iOS自定义带动画效果的模态框
CocoaChina
7+阅读 · 2019年3月3日
可能是 Android 上最好用的写作 App
少数派
10+阅读 · 2018年12月21日
Android P正式发布,你需要尽快做适配了
前端之巅
3+阅读 · 2018年8月7日
Python 杠上 Java、C/C++,赢面有几成?
CSDN
6+阅读 · 2018年4月12日
Top
微信扫码咨询专知VIP会员