Swift YDRootNavigationController

2024-03-27  本文已影响0人  jsone

简介

YDRootNavigationController 是一个使用Swift语言和面向协议编程思想对原生的导航栏控制进行再封装的基础导航栏控制器库,它支持全局设置默认的导航栏样式同时支持视图控制器自定义导航栏样式,使用方法简单,可以按照UINavigationController的使用方法去使用,不添加额外的控制器层级。

YDRootNavigationController

做过iOS开发的人都知道, 原生的导航栏样式设置有很多局限,如果是整个App都是统一的导航栏样式还好, 要是存在两种或两种以上的样式就非常麻烦了,因为同一个导航栏控制器的视图控制器的导航栏是共用的,一旦改了导航栏的样式就会影响到其他视图控制器的导航栏样式,例如,有个App默认全局是显示导航栏,但是当前视图控制器需要隐藏导航栏,其他视图控制器有需要隐藏有的不需要,如果设置隐藏后,push到的控制器的不隐藏导航栏的,这时就出现bug了,于是你在viewDidDisappear方法里面设置显示导航栏后,这时控制器pop到前一个控制器也是隐藏导航栏的,这时候就又又出现bug了,尤其是团队开发的App,经常因为这个问题导致出现bug。
以前使用Objective-C开发时,我们可能会用到 RTRootNavigationController 这个库,它支持每个视图控制器拥有自己的导航栏,这样不同控制器的导航栏样式设置就互不影响,而且支持原生的返回手势开启与关闭,但是由于它是为每个视图控制器都要再包一层导航栏控制器和一层视图控制器,这样的弊端就是原来的视图控制器的层级结构发生变化,每个视图控制器都要包两层控制器,这就造成了冗余和资源浪费,原来的一些属性和用法就失效或者替换成其他属性,无法按照原生的UINavigationController的使用方法去使用导航栏控制器,例如 ,当前控制器的navigationController返回的就不是你所需要的那个外层的导航栏控制器,所以在学习Swift语言后,我就有一个想法:是不是可以自己用Swift语言封装一个在保留原生的UINavigationController的使用方法,尽量不用添加其他的层级,支持配置全局默认的导航栏样式的同时支持每个控制器可以自定义导航栏的Pod库。

RTRootNavigationController

后来,在使用Swift语言开发项目中,由于当时并没有比较成熟的关于导航栏设置的第三方库,所以项目中都是用最原始的方法,例如:导航栏的显示与隐藏,每个控制器直接在viewWillAppear里面直接设置,由于是一个团队在开发一款App,而且导航栏样式比较多,每个人又负责不同的页面,有的页面要显示有的又要隐藏导航栏,页面跳转路径又不固定,就会经常出现该显示导航栏的页面没显示,该隐藏导航栏的页面没隐藏等各种相关的bug。于是乎,我就将存放在心里已久的想法付诸实践,从一开始只支持管理导航栏的显示与隐藏,到后来支持返回手势、配置全局默认导航栏样式、视图控制器自定义导航栏样式、返回按钮、标签栏的管理,整个库的功能基本稳定可靠。项目也从之前出现导航栏显示与隐藏各种相关的bug,到后来完全不再出现相关问题,团队也不用浪费时间在相关问题上,日常有关开发的效率也得到了提高。

概览

YDRootNavigationController架构图

原理

全局默认设置

YDAppAppearanceProtocol协议定义了一些全局默认配置的属性,其中包含了导航栏、标签栏、工具栏样式的设置,还有显示隐藏状态、返回手势等一些属性的设置。

/// 全局默认配置
public protocol YDAppAppearanceProtocol {
    var barItemNormalTitleTextAttributes: [NSAttributedString.Key : Any]? { get }
    var barItemHighlightedTitleTextAttributes: [NSAttributedString.Key : Any]? { get }
    var barItemDisableTitleTextAttributes: [NSAttributedString.Key : Any]? { get }
    /// 导航栏标题文字属性
    var titleTextAttributes: [NSAttributedString.Key: Any]? { get }
    /// 导航栏背景颜色
    var navigationBarBackgroundColor: UIColor? { get }
    /// 导航栏背景图片(设置背景图片后,如果有设置背景颜色,背景颜色将失效)
    var navigationBarBackgroundImage: UIImage? { get }
    /// 导航栏阴影颜色
    var navigationBarShadowColor: UIColor? { get }
    /// 工具栏背景颜色
    var toolBarBackgroundColor: UIColor? { get }
    var toolBarShadowColor: UIColor? { get }
    var tabBarBackgroundColor: UIColor? { get }
    var tabBarShadowColor: UIColor? { get }
    /// 返回按钮图片
    var backItemImage: UIImage? { get }
    /// 返回按钮图片内边距
    var backItemImageInsets: UIEdgeInsets? { get }
    var isHidesBackItem: Bool { get }
    /// 设置是否隐藏导航栏(默认关闭)
    var prefersNavigationBarHidden: Bool { get }
    /// (默认打开)设置导航栏控制器push时,是否隐藏标签栏(当导航栏控制器是标签栏控制器的子控制器时)
    var isHidesBottomBarWhenPushed: Bool { get }
    /// 设置侧滑返回手势是否开启(默认打开)
    var isInteractivePopGestureEnabled: Bool { get }
    /// 设置全屏返回手势是否开启(默认关闭)
    var isFullScreenPopGestureEnabled: Bool { get }
}

通过YDAppAppearanceProtocol的扩展实现相关属性的默认设置

public extension YDAppAppearanceProtocol {
    var barItemNormalTitleTextAttributes: [NSAttributedString.Key : Any]? { nil }
    var barItemHighlightedTitleTextAttributes: [NSAttributedString.Key : Any]? { nil }
    var barItemDisableTitleTextAttributes: [NSAttributedString.Key : Any]? { nil }
    var titleTextAttributes: [NSAttributedString.Key: Any]? { nil }
    var navigationBarBackgroundColor: UIColor? { nil }
    var navigationBarBackgroundImage: UIImage? { nil }
    var navigationBarShadowColor: UIColor? { nil }
    var toolBarBackgroundColor: UIColor? { nil }
    var toolBarShadowColor: UIColor? { nil }
    var tabBarBackgroundColor: UIColor? { nil }
    var tabBarShadowColor: UIColor? { nil }
    var backItemImage: UIImage? { nil }
    var backItemImageInsets: UIEdgeInsets? { nil }
    var isHidesBackItem: Bool { false }
    var prefersNavigationBarHidden: Bool { false }
    var isHidesBottomBarWhenPushed: Bool { true }
    var isInteractivePopGestureEnabled: Bool { true }
    var isFullScreenPopGestureEnabled: Bool { false }
    
    func configure() {…}
}

其中,configure()方法是用来配置导航栏、标签栏、工具栏默认样式生效的方法,该方法就是通过将协议属性返回的值设置到对应的原生导航栏、标签栏、工具栏appearance中,开发中只要有一个类实现YDAppAppearanceProtocol协议,如果属性值跟默认设置是一样的,即可以不用实现这个属性,然后在AppDelegate中调用configure()方法,就可以实现全局默认的导航栏、标签栏、工具栏样式。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // 全局默认样式配置
        MyAppAppearance().configure()
        return true
    }
}

视图控制器自定义设置

YDViewControllerProtocol协议定义了一些视图控制器自定义的属性

@objc public protocol YDViewControllerProtocol {
    /// 是否隐藏返回按钮
    var isHidesBackItem: Bool { get }
    /// 是否隐藏导航栏
    var prefersNavigationBarHidden: Bool { get }
    /// push时是否隐藏标签栏
    var isHidesBottomBarWhenPushed: Bool { get }
    /// 设置侧滑返回手势是否开启(用于控制器显示前的默认设置)
    var isInteractivePopGestureEnabled: Bool { get }
    /// 设置全屏返回手势是否开启(用于控制器显示前的默认设置)
    var isFullScreenPopGestureEnabled: Bool { get }
    /// 自定义返回按钮
    var backItem: UIBarButtonItem? { get }
    /// 定制返回按钮样式
    var backItemType: YDBackItemType { get }
    /// 导航栏样式(背景颜色、标题文字属性、阴影颜色)
    var navigationBarAppearence: YDNavigationBarAppearence { get }
    /// 自定义返回按钮点击事件
    @objc func backItemAction(_ sender: Any?)
}

通过UIViewController扩展实现YDViewControllerProtocol来设置相关属性方法的默认值和实现

extension UIViewController: YDViewControllerProtocol {
    open var isHidesBackItem: Bool { YDRootNavigationController.appAppearance.isHidesBackItem }
    open var prefersNavigationBarHidden: Bool { YDRootNavigationController.appAppearance.prefersNavigationBarHidden }
    open var isHidesBottomBarWhenPushed: Bool { YDRootNavigationController.appAppearance.isHidesBottomBarWhenPushed }
    open var isInteractivePopGestureEnabled: Bool { YDRootNavigationController.appAppearance.isInteractivePopGestureEnabled }
    open var isFullScreenPopGestureEnabled: Bool { YDRootNavigationController.appAppearance.isFullScreenPopGestureEnabled }
    open var backItem: UIBarButtonItem? { nil }
    open var backItemType: YDBackItemType { .default() }
    open var navigationBarAppearence: YDNavigationBarAppearence {
        YDNavigationBarAppearence()
    }

    @objc open func backItemAction(_ sender: Any?) {
        navigationController?.popViewController(animated: true)
    }
    
    /// 设置侧滑返回手势是否开启(用于控制器显示后动态设置)
    /// - Parameter enable: 是否开启
    public func interactivePopGesture(_ isEnabled: Bool) {
        guard let navigationController = self.navigationController as? YDRootNavigationController else { return }
        navigationController.setInteractivePopGesture(isEnabled)
    }
    
    /// 设置全屏返回手势是否开启(用于控制器显示后动态设置)
    /// - Parameter enable: 是否开启
    public func fullScreenPopGesture(_ isEnabled: Bool) {
        guard let navigationController = self.navigationController as? YDRootNavigationController else { return }
        navigationController.setFullScreenPopGesture(isEnabled)
    }
}

YDRootNavigationController的类属性appAppearance为全局默认配置,如果没有实现YDAppAppearanceProtocol协议,就会生成一个协议默认实现类的实例对象。

open class YDRootNavigationController: UINavigationController {
    /// 全局默认设置
    static var appAppearance: YDAppAppearanceProtocol = YDAppAppearance()
}

YDRootNavigationController扩展实现UINavigationControllerDelegate协议,在实现协议的方法中去设置自定义导航栏的样式和显示状态,或者按照全局默认配置去设置,使每个视图控制器都有独立的导航栏样式、返回手势等其他相关配置,这个实现思路是参考了RTRootNavigationController ,但却不用插入额外的导航栏和视图控制器,保留了原生UINavigationController的使用方法,全屏返回手势的功能则是参考了 FDFullscreenPopGesture 的实现方法。YDRootNavigationController就是用Swift的面向协议编程将这两个库的功能完美的结合在一起,并扩展了功能。

extension YDRootNavigationController: UINavigationControllerDelegate {
    
    open func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
    }
    
    open func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
    }
    ……
}

具体使用方法可以查看这篇文章 Swift 全局默认导航栏样式与视图控制器自定义并存

上一篇下一篇

猜你喜欢

热点阅读