🔥 NXNavigationExtension 是为 UINavigationBar 设计的轻量级的、简单的、可扩展的库,支持 SwiftUI 和 UIKit。框架对现有代码入侵非常小,只需要简单的几个方法调用就可以满足大部分的应用场景。可能是最省心的 iOS 导航栏处理框架之一。NXNavigationExtension 框架本身和示例代码都已经适配暗黑模式可供大家参考。
🎉 预览
| SwiftUI 路由 | 完全自定义导航栏 | 返回事件拦截 | 修改导航栏外观 | 
|---|---|---|---|
|  |  |  |  | 
🌈 要求
| Version | Minimum iOS / macOS Target | Frameworks | Note | 
|---|---|---|---|
| 4.1.7 Later | iOS 12.0 / macOS 10.15 | SwiftUI、UIKit、macCatalyst | Xcode15 | 
| 4.1.5 Later | iOS 11.0 / macOS 10.15 | SwiftUI、UIKit、macCatalyst | Xcode14 | 
| 4.1.4 | iOS 9.0 / macOS 10.15 | SwiftUI、UIKit、macCatalyst | Xcode13 | 
| 3.x | iOS 9.0 / macOS 10.15 | UIKit、macCatalyst | / | 
| 2.x | iOS 11.0 / macOS 10.15 | UIKit、macCatalyst | / | 
🍭 优点
- 没有对原生导航栏视图层级进行修改,无需担心系统升级的兼容性问题。
- API 设计通俗易懂,容易使用。
- 没有继承关系,所有操作基于分类实现,低耦合。
- 白名单模式:按需注册所使用的导航控制器,这样才不会影响所有的导航控制器外观。
- 适配 iOS、iPadOS、macOS、横竖屏切换、暗黑模式、支持阿拉伯语等从右往左的布局方式。
- 提供 SwiftUI、UIKit、macCatalyst 框架的支持。
- 支持 CocoaPods、Carthage、Project、Swift Package Manager 方式集成。
👏 功能
下面这些特别实用的功能,总有一部分适合你的项目
基本功能
- ✅设置导航栏透明
- ✅实现系统导航栏模糊效果
- ✅自定义返回按钮图片
- ✅自定义返回按钮
- ✅自定义导航栏模糊背景
- ✅修改返回按钮箭头颜色
- ✅修改系统返回按钮文字
- ✅修改导航栏标题颜色
- ✅修改导航栏背景颜色
- ✅修改导航栏背景图片
- ✅设置导航栏底部阴影颜色
- ✅设置导航栏底部阴影图片
高级功能
- ✅禁用滑动返回手势
- ✅启用全屏滑动返回手势
- ✅导航栏返回事件拦截
- ✅支持视图控制器转场周期事件
- ✅任一视图控制器跳转
- ✅SwiftUI 路由
- ✅导航栏点击事件穿透到底部
- ✅动态修改导航栏样式
- ✅更新导航栏样式
- ✅渐变导航栏样式
- ✅长按返回按钮显示菜单功能
- 更多功能请查看示例代码...
🌟 开始使用
下载 NXNavigationExtension 示例代码。
使用 CocoaPods 集成
使用 CocoaPods 将 NXNavigationExtension 集成到 Xcode 项目中,需要在 Podfile 中指定:
## For SwiftUI
pod 'NXNavigationExtension/SwiftUI'
## For UIKit
pod 'NXNavigationExtension'使用 Carthage 管理
使用 Carthage 管理 NXNavigationExtension framework,请将以下内容添加到您的 Cartfile 文件中:
# For SwiftUI
github "l1Dan/NXNavigationExtension" # Requires
github "l1Dan/NXNavigationExtensionSwiftUI"
# For UIKit
github "l1Dan/NXNavigationExtension"
使用 Swift Package Manager 集成
使用 Swift Package Manager 集成 NXNavigationExtension,请将以下内容添加到您的 Package.swift 文件的依赖中:
dependencies: [
    .package(url: "https://github.com/l1Dan/NXNavigationExtension.git", .upToNextMajor(from: "4.1.7"))
]📖 使用教程
- 阅读 SwiftUI Guide 文档。
- 阅读 UIKit Guide 文档。
🍽 使用
所有对导航栏外观的修改都是基于视图控制器 UIViewController 修改的,而不是基于导航控制器 UINavigationController 修改,这种设计逻辑更加符合实际应用场景。也就是说视图控制器管理自己的导航栏,而不是使用导航控制器来全局管理。
- 💉 导入模块 import NXNavigationExtension。
- 💉 使用之前需要先在 AppDelegate中注册需要修改的导航控制器。
✅ 推荐
只需要下面这一行代码就完成导航控制器的注册,现在你可以尽情地修改导航栏的外观了。
// For YourNavigationController
NXNavigationConfiguration().registerNavigationControllerClasses([YourNavigationController.self])或者
当然也可以同时注册多个导航控制器
NXNavigationConfiguration().registerNavigationControllerClasses([YourNavigationController.self, YourNavigationController2.self])或者
还可以动态修改导航栏的外观(NXNavigationExtensionSwiftUI 框架就是基于这个特性实现的)
let configuration = NXNavigationConfiguration.default
// 默认首选配置(单利对象意味着对所有注册的导航控制器生效)
configuration.navigationBarAppearance.tintColor = .customTitle
configuration.registerNavigationControllerClasses([YourNavigationController.self, YourNavigationController2.self]) { navigationController, configuration in
    // For UINavigationController,针对不同导航控制器的修改
    if navigationController is YourNavigationController {
        // 动态修改导航控制器的默认首选配置
        configuration.navigationBarAppearance.backgroundColor = .brown
        // 动态修改视图控制器的默认首选配置
        navigationController.nx_prepareConfigureViewControllersCallback { viewController, configuration in
            // For UIViewController 针对不同视图控制器的修改
            if viewController is MyViewController {
                configuration.navigationBarAppearance.backgroundColor = .red
            }
        }
    }
    return configuration
}❌ 不推荐
// UINavigationController 会影响所有的导航控制器,所以不推荐使用这种方式注册
NXNavigationConfiguration().registerNavigationControllerClasses([UINavigationController.self])注意:
- 👉 虽然示例程序代码使用的是 Swift语言实现的,但框架还是可以支持Objective-C语言的,如果需要Objective-C示例程序的代码可以查看 3.x 分支代码。
- 👉 使用这个框架之前需要先注册导航控制器,然后再去修改被注册的导航控制器所管理的视图控制器的导航栏外观。
- 👉 为了有效避免框架污染到其他的导航控制器,请保持“谁使用,谁注册”的原则。
- 🚫 不要直接注册 UINavigationController,会影响全局导航栏的外观,建议创建一个UINavigationController的子类,对这个子类进行外观的设置。
- 🚫 不要使用 setNavigationBarHidden:、setNavigationBarHidden:animated、setHidden:等方法显示或隐藏系统导航栏。
- 🚫 不要使用系统导航栏修改透明度。
- 🚫 不要使用系统导航栏或导航控制器 appearance相关属性修改。
- 🚫 不要使用 <UIGestureRecognizerDelegate>相关方法禁用手势返回。
- ❗️ 在某些条件下,不推荐使用 UIViewController 的 edgesForExtendedLayout = UIRectEdge(rawValue: 0)属性设置,使用默认方式即可,具体原因请查看。
- 一句话总结:原则就是不要直接修改系统导航栏或者导航控制器的外观,可以让我们少走弯路,把这些繁琐的事情都交给这个框架处理吧。
🍻 基本功能
修改返回按钮箭头颜色
📝 示例代码
override var nx_barTintColor: UIColor? {
    return isDarkMode ? .white : .black
}修改系统返回按钮文字
📝 示例代码
// 需要设置使用系统返回按钮,这样才会有效果
override var nx_useSystemBackButton: Bool {
    return true
}
override var nx_systemBackButtonTitle: String? {
    return backButtonTitle
}修改导航栏标题颜色
📝 示例代码
override var nx_titleTextAttributes: [NSAttributedString.Key : Any]? {
    return [NSAttributedString.Key.foregroundColor: nx_barTintColor ?? (isDarkMode ? .white : .black)]
}修改导航栏背景颜色
导航栏背景颜色默认使用系统蓝色 UIColor.systemBlue,这样处理能够快速辨别框架是否生效,也可以使用以下方式进行重写:
📝 示例代码
// 全局统一修改(不会覆盖基于视图控制器的修改)
let configuration = NXNavigationConfiguration.default
configuration.navigationBarAppearance.backgroundColor = .red
// 基于视图控制器修改(可以是基类视图控制器也是可以是特定需要修改的视图控制器)
override var nx_navigationBarBackgroundColor: UIColor? {
    return randomColor
}修改导航栏背景图片
📝 示例代码
override var nx_navigationBarBackgroundImage: UIImage? {
    return UIImage.navigationBarBackground
}设置导航栏透明
📝 示例代码
override var nx_navigationBarBackgroundColor: UIColor? {
    return .clear
}
// 设置导航栏底部阴影颜色
override var nx_shadowColor: UIColor? {
    return .clear
}实现系统导航栏模糊效果
📝 示例代码
override var nx_navigationBarBackgroundColor: UIColor? {
    return .clear
}
override var nx_useBlurNavigationBar: Bool {
    return true
}设置导航栏底部阴影颜色
📝 示例代码
override var nx_shadowColor: UIColor? {
    return .red
}设置导航栏底部阴影图片
📝 示例代码
override var nx_shadowImage: UIImage? {
    return UIImage(named: "NavigationBarShadowImage")
}自定义返回按钮图片
📝 示例代码
override var nx_backImage: UIImage? {
    return UIImage(named: "NavigationBarBack")
}自定义返回按钮
📝 示例代码
override var nx_backButtonCustomView: UIView? {
    return backButton
}🍺 高级功能
禁用滑动返回手势
📝 示例代码
override var nx_disableInteractivePopGesture: Bool {
    return true
}启用全屏滑动返回手势
📝 示例代码
- 局部有效(在所处页面设置)
override var nx_enableFullScreenInteractivePopGesture: Bool {
    return true
}- 全局有效
let configuration = NXNavigationConfiguration.default
configuration.viewControllerPreferences.enableFullScreenInteractivePopGesture = true设置导航栏隐藏(并不是真的隐藏,只是看起来隐藏了,整个导航栏区域不能处理用户交互)
📝 示例代码
// 此操作会将导航栏的背景设置为透明、导航栏所在区域的底部能够接收到点击事件、返回按钮也将不存在。
// “隐藏”导航栏时不要添加 UINavigationBar 的 barButtonItem(s),这样就可以看起来真的像导航栏隐藏了。
// 不隐藏系统导航栏的原因是:可以让整个导航栏的过渡更加平滑自然,当然也不推荐除此之外任何隐藏系统导航栏的方式。
override var nx_translucentNavigationBar: Bool {
    return true
}禁用系统导航栏用户交互(NXNavigationBar 可以处理用户交互)
📝 示例代码
override var systemNavigationBarUserInteractionDisabled: Bool {
    return true
}更新导航栏样式
📝 示例代码
nx_setNeedsNavigationBarAppearanceUpdate()如果状态栏样式没有发生变化,请检查是否需要调用方法 setNeedsStatusBarAppearanceUpdate(),或者在 UINavigationController 的子类中设置如下代码:
override var childForStatusBarStyle: UIViewController? {
    return topViewController
}
override var childForStatusBarHidden: UIViewController? {
    return topViewController
}导航栏返回事件拦截
📝 示例代码
需要遵守协议 <NXNavigationControllerDelegate>,实现代理方法:
- NXNavigationInteractiveTypeCallNXPopMethod: 调用- nx_pop系列方法返回事件拦截。
- NXNavigationInteractiveTypeBackButtonAction: 点击返回按钮返回事件拦截。
- NXNavigationInteractiveTypeBackButtonMenuAction: 长按返回按钮选择菜单返回事件拦截。
- NXNavigationInteractiveTypePopGestureRecognizer: 使用手势交互返回事件拦截。
func nx_navigationController(_ navigationController: UINavigationController, willPop viewController: UIViewController, interactiveType: NXNavigationInteractiveType) -> Bool {
    switch interactiveType {
    case .backButtonAction:
        // Do something
        return false
    case .backButtonMenuAction:
        // Do something
        return false
    case .popGestureRecognizer:
        // Do something
        return false
    case .callNXPopMethod:
        // Do something
        return false
    default:
        // Continue back
        return true
    }
}自定义返回按钮事件需要拦截可以调用 nx_popViewControllerAnimated:、nx_popToViewController:animated: 或 nx_popToRootViewControllerAnimated: 等方法来触发上面的代理回调。
支持视图控制器转场周期事件
需要遵守协议 <NXNavigationControllerDelegate>,实现代理方法:
func nx_navigationController(_ navigationController: UINavigationController, processViewController viewController: UIViewController, navigationAction: NXNavigationAction) {
    switch navigationAction {
    case .unspecified: print("Unspecified")
    case .willPush: print("WillPush")
    case .didPush: print("DidPush")
    case .pushCancelled: print("PushCancelled")
    case .pushCompleted: print("PushCompleted")
    case .willPop: print("WillPop")
    case .didPop: print("DidPop")
    case .popCancelled: print("PopCancelled")
    case .popCompleted: print("PopCompleted")
    case .willSet: print("WillSet")
    case .didSet: print("DidSet")
    case .setCancelled: print("SetCancelled")
    case .setCompleted: print("SetCompleted")
    default: print("None")
    }
}任一视图控制器跳转
📝 示例代码
- 以跳转到 ViewController08_JumpToViewController为例,如果之前有 Push 过ViewController08_JumpToViewController的实例,则最后会跳转到这个视图控制器中,如果没有找到则会调用block执行插入新控制器的规则。
- 执行此操作之后,并不会跳转到对应的视图控制器,仅仅是修改了 NavigationController 的 viewControllers 属性,如果需要跳转操作,可以调用 pop系列方法返回上一个页面,也可以使用手势滑动返回页面,还可以点击返回按钮返回页面。
navigationController?.nx_setPreviousViewController(with: ViewController08_JumpToViewController.self, insertsInstanceToBelowWhenNotFoundUsing: {
    return ViewController08_JumpToViewController()
})
// 执行视图控制器跳转操作:
navigationController?.popViewController(animated: true)意思是:首先查找 navigationController?.viewControllers 是否存在一个类型为 ViewController08_JumpToViewController.self 的实例对象,如果存在则上一页面会显示此视图控制器,没有找到则使用 ViewController08_JumpToViewController() 创建一个新的实例对象插入到 NavigationController 的 viewControllers 栈的上一个页面中。
长按返回按钮显示菜单功能
📝 示例代码
override var nx_useSystemBackButton: Bool {
    return true
}❤️ 感谢 JetBrains 对开源项目的支持
⭐️ 参考链接
📄 协议
NXNavigationExtension 框架是在 MIT 许可下发布的。详情请参见 LICENSE。

