🔥 NXNavigationExtension 是为 iOS 应用设计的一个轻量级的导航栏处理框架,同时支持 SwiftUI 和 UIKit。框架对现有代码入侵非常小,只需要简单的几个方法调用就可以满足大部分的应用场景。可能是最省心的 iOS 导航栏处理框架之一。NXNavigationExtension 框架本身和示例代码都已经适配暗黑模式可供大家参考。先附上地址: github.com/l1Dan/NXNav… ,欢迎 Star 🌟 和 PR。
🎉 预览
NXNavigationExtension Version | Minimum iOS Target | Minimum macOS Target | Frameworks | Note |
---|---|---|---|---|
4.x | iOS 9.0 | macOS 10.15 | SwiftUI、UIKit、macCatalyst | Xcode13、SwiftUI 2.0 |
3.x | iOS 9.0 | macOS 10.15 | UIKit、macCatalyst | / |
2.x | iOS 11.0 | macOS 10.15 | UIKit、macCatalyst | / |
👏 功能
下面这些特别实用的功能,总有一部分适合你的项目
设置导航栏透明
实现系统导航栏模糊效果
自定义返回按钮图片
自定义返回按钮
自定义导航栏模糊背景
修改返回按钮箭头颜色
修改系统返回按钮文字
修改导航栏标题颜色
修改导航栏背景颜色
修改导航栏背景图片
修改导航栏底部线条颜色
修改导航栏底部线条图片
禁用滑动返回手势
启用全屏滑动返回手势
导航栏返回事件拦截
重定向任一控制器跳转
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.0.2"))
NXNavigationExtensionSwiftUI
由 NXNavigationExtension 框架提供强力支持,他们的功能基本保持一致。注册完成之后需要在 NavigationView
中指定使用 .navigationViewStyle(.stack)
风格,但是目前仅支持 iOS 14 及以上系统的 StackNavigationViewStyle
风格,其他系统和 NavigationViewStyle
后续会不断完善。
下面是框架对 StackNavigationViewStyle
风格和 iOS 系统版本的支持情况:
NavigationViewStyle / iOS version iOS 13 iOS 14 iOS 15 .automatic ❌ ❌ ❌ .stack ❌ ✅ ✅ .columns ❌ ❌ ❌
💉 导入模块。
使用 CocoaPods
集成:import NXNavigationExtension
使用 Carthage
管理:import NXNavigationExtensionSwiftUI
💉 使用之前需要先在 AppDelegate
中注册需要修改的导航控制器。
在 UIKit
版本中其实只需要 NXNavigationConfiguration().registerNavigationControllerClasses([YourNavigationController.self])
这一行代码就完成导航控制器的注册了。但是在 SwiftUI
版本中还需要一个额外的步骤:指定 NXNavigationVirtualWrapperView
的查找规则,他是 SwiftUI 与 UIKit 之间的桥梁。查找规则开发者可以自定义,也可以使用框架提供的默认查找规则。
// AppDelegate.swift
var classes: [AnyClass] = []
if #available(iOS 15.0, *) {
classes = [
NSClassFromString("SwiftUI.SplitViewNavigationController"),
NSClassFromString("SwiftUI.UIKitNavigationController"),
].compactMap { $0 }
} else {
classes = [
NSClassFromString("SwiftUI.SplitViewNavigationController"), // iOS14
].compactMap { $0 }
let defaultConfiguration = NXNavigationConfiguration.default
defaultConfiguration.registerNavigationControllerClasses(classes) { navigationController, configuration in
// Configure
navigationController.nx_applyFilterNavigationVirtualWrapperViewRuleCallback(NXNavigationVirtualView.configureWithDefaultRule(for:))
return configuration
// Example: ContentView.swift
import SwiftUI
import NXNavigationExtension
struct DestinationView: View {
@State private var context: NXNavigationRouter.Context = NXNavigationRouter.Context(routeName: "/destinationView")
var body: some View {
Button {
// NXNavigationRouter.of(context).pop()
NXNavigationRouter.of(context /* /destinationView */).popUntil("/contentView")
} label: {
Text("Pop")
.padding()
.useNXNavigationView(context: $context, onPrepareConfiguration: { configuration in
// `DestinationView` NavigationView backgroundColor
configuration.navigationBarAppearance.backgroundColor = .red
struct ContentView: View {
@State private var context: NXNavigationRouter.Context = NXNavigationRouter.Context(routeName: "/contentView")
var body: some View {
NavigationView {
NavigationLink { // 1. 使用 NavigationView 包装
DestinationView()
} label: {
Text("Push")
.padding()
.useNXNavigationView(context: $context /* /contentView */, onPrepareConfiguration: { configuration in
// 3. 修改导航栏背景颜色 ... `Text` NavigationView backgroundColor
configuration.navigationBarAppearance.backgroundColor = .brown
.navigationViewStyle(.stack) // 2. 使用 StackNavigationViewStyle 风格
🍻 基本功能
修改返回按钮箭头颜色
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.tintColor = .red
修改系统返回按钮文字
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.useSystemBackButton = true
configuration.navigationBarAppearance.systemBackButtonTitle = title
修改导航栏标题颜色
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.black]
修改导航栏背景颜色
导航栏背景颜色默认使用系统蓝色 UIColor.systemBlue
,这样处理能够快速辨别框架是否生效,也可以使用以下方式进行重写:
📝 示例代码
// 全局统一修改
let configuration = NXNavigationConfiguration.default
configuration.navigationBarAppearance.backgroundColor = .red
// 基于视图控制器修改
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backgroundColor = .red
修改导航栏背景图片
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backgroundImage = UIImage(named: "NavigationBarBackground88")
设置导航栏透明
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backgroundColor = .clear
configuration.navigationBarAppearance.shadowColor = .clear
实现系统导航栏模糊效果
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backgroundColor = .clear
configuration.viewControllerPreferences.useBlurNavigationBar = true
修改导航栏底部线条颜色
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backgroundColor = .systemBackground
configuration.navigationBarAppearance.shadowColor = .systemRed
修改导航栏底部线条图片
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backgroundColor = .systemBackground
configuration.navigationBarAppearance.shadowImage = UIImage(named: "NavigationBarShadowImage")
自定义返回按钮图片
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backImage = UIImage(systemName: "arrow.left")
自定义返回按钮
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.backButtonCustomView = backButton
🍺 高级功能
禁用滑动返回手势
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.viewControllerPreferences.disableInteractivePopGesture = true
启用全屏滑动返回手势
📝 示例代码
let configuration = NXNavigationConfiguration.default
configuration.viewControllerPreferences.enableFullScreenInteractivePopGesture = true
局部有效(在所处页面设置)
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.viewControllerPreferences.enableFullScreenInteractivePopGesture = true
导航栏返回事件拦截
📝 示例代码
.callNXPopMethod
: 调用 nx_pop
系列方法返回事件拦截。
.backButtonAction
: 点击返回按钮返回事件拦截。
.backButtonMenuAction
: 长按返回按钮选择菜单返回事件拦截。
.popGestureRecognizer
: 使用手势交互返回事件拦截。
Text("Destination")
.useNXNavigationView(onWillPopViewController: { interactiveType in
if selectedItemType == .backButtonAction && interactiveType == .backButtonAction ||
selectedItemType == .backButtonMenuAction && interactiveType == .backButtonMenuAction ||
selectedItemType == .popGestureRecognizer && interactiveType == .popGestureRecognizer ||
selectedItemType == .callNXPopMethod && interactiveType == .callNXPopMethod ||
selectedItemType == .all {
isPresented = true
return false
return true
SwiftUI 路由
📝 示例代码
struct DestinationView: View {
@State private var context: NXNavigationRouter.Context
init() {
context = NXNavigationRouter.Context(routeName: "/currentRouteName")
var body: some View {
VStack {
Button {
NXNavigationRouter.of(context).pop()
} label: {
Text("Pop")
.useNXNavigationView(context: $context)
需要注意的是 NXNavigationRouter.of(context)
和 NXNavigationRouter.of(context).nx
用于调用系统 pop
和框架 nx_pop
系列方法
使用 NXNavigationRouter.of(context).nx
方法弹出页面时会触发 onWillPopViewController
的回调。
导航栏点击事件穿透到底部
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.viewControllerPreferences.translucentNavigationBar = true
更新导航栏样式
📝 示例代码
Button {
NXNavigationRouter.of(context).setNeedsNavigationBarAppearanceUpdate()
} label: {
Text("Update")
长按返回按钮显示菜单功能
📝 示例代码
Text("Destination")
.useNXNavigationView(onPrepareConfiguration: { configuration in
configuration.navigationBarAppearance.useSystemBackButton = true
FAQ 常见问题
Q:iOS14 及之后的版本为什么注册了 UIImagePickerController
、PHPickerViewController
类之后还是无法修改导航栏的外观?
A:因为 UIImagePickerController
和 PHPickerViewController
里面的 UINavigationBar 是隐藏的,NXNavigationBar 会跟随系统导航栏隐藏与显示,所以无法修改(iOS14 之前系统的 UIImagePickerController
是可以修改的)。另外 PHPickerViewController 其实是一个 UIViewController 的子类,你既可以用 push
的方式显示控制器也可以用 present
的方式显示控制器,他们有个共同特点:使用的都是一个 “假” 的导航栏。
Q:为什么 iOS13 之前使用 self.navigationItem.searchController
设置的 UISearchBar
无法跟随导航栏的变化而变化,iOS13 之后的却可以呢?
A:因为在 iOS13 之前导航栏中不包含 UISearchBar
,iOS13 之后导航栏才包含 UISearchBar
的。具体使用请参考示例代码。
Q:如何解决 UIScrollView
和 UIPageViewController
全屏手势冲突?
A:使用 UIScrollView 和 UIPageViewController 全屏手势冲突解决方案。
Q:为什么 NXNavigationExtension
框架不包含控制器的转场动画功能?
A:原则就是尽可能的保持框架的简单轻量,将更多的精力花在框架本身的稳定性上,尽可能地使用系统原有功能。转场动画功能并不适用于所有业务场景,另外也不属于这个框架的功能。如果有转场动画的需求需要开发者自己实现,也可以参考VCTransitionsLibrary,或者参考示例代码。
Q:为什么导航栏的系统返回按钮箭头和自定义返回按钮箭头的位置不一致?
A:因为导航栏的系统返回按钮是用 self.navigationItem.backBarButtonItem
属性来设置的。而自定义返回按钮是用 self.navigationItem.leftBarButtonItem
属性来设置的,他们的位置本来就不一样。当然你可以使用系统返回按钮,通过 (nx_)useSystemBackButton
属性设置是否使用系统返回按钮,再配合 (nx_)systemBackButtonTitle
属性设置系统返回按钮的标题。还可以通过 (nx_)backImageInsets
或者 (nx_)landscapeBackImageInsets
属性来控制自定义返回按钮图片的偏移量。
返回按钮箭头在切图里尽量靠左而不要居中,右边可以保留透明背景。
使用 nx_backButtonCustomView
属性自定义返回按钮时就需要开发者自己来修正箭头的偏移量了。
📄 协议
NXNavigationExtension 框架是在 MIT 许可下发布的。详情请参见 LICENSE。