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

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

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

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

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

修改系统导航栏

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

优势:

  1. 实现好后,各控制器定制起来调用方便,每每一两行代码就能够了。
  2. 可以保留侧滑返回的导航栏过渡效果(这个依需求而定,也并彻底算优势)

缺点:

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

这种方式可参考这几篇中文分享,写得很是详细:ide

彻底使用自定义导航栏

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

优势

  1. 避免系统导航栏存在的各类未知坑
  2. 实现效果可高度自定义,高兴的话能够设计成波浪形,还带动画交互的那种
  3. 通常有些应用采用底部导航栏的设计,基本都是彻底使用自定义导航栏实现

缺点

  1. 通常没有系统导航栏的侧滑过渡效果,可参考手淘。(不算彻底意义上的缺点)
  2. 依据不一样的需求和实现方式,工做量可能较大
  3. 侧滑返回手势、滑动隐藏、触控隐藏等一些系统交互需自行实现
  4. 须要额外处理系统导航栏可以自动处理的in call等系统响应

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

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

优势

  1. 避免修改系统导航栏可能遇到的坑
  2. 仅部分页面针对性采用自定义导航栏,工做量相对可控
  3. 采用系统导航栏的页面之间保留侧滑过渡效果

缺点

  1. 如果须要自定义导航栏的页面较多,工做增量较大
  2. 自定义导航栏页面的侧滑返回等效果须要额外处理

小结

总的来讲,三种方式各有优缺,主要仍是按照不一样的需求采用不一样的方案,如果导航栏真的须要水波烂漫的交互效果,侧滑返回的时候还要有个小船划回去,这若非要挑战经过修改系统导航栏的方式实现,费劲踩坑估计在所不免。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)
        }
    }复制代码

处理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项目。如果但愿继续了解修改系统导航栏的实现方式,可参考文中所说起的几篇分享,强烈推荐。

因此你偏好哪一种方式呢?

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

博客原文连接

相关文章
相关标签/搜索