CustomNavBar 自定义导航栏视图

1. 创建偏好设置键 CustomNavBarTitlePreferenceKey.swift

Swift 复制代码
import Foundation
import SwiftUI

//@State private var showBackButton: Bool = true
//@State private var title: String = "Title" //""
//@State private var subtitle: String? = "SubTitle" //nil

/// 导航栏标题偏好设置
struct CustomNavBarTitlePreferenceKey: PreferenceKey{
    static var defaultValue: String = ""
    
    static func reduce(value: inout String, nextValue: () -> String) {
        value = nextValue()
    }
}

/// 导航栏子标题偏好设置
struct CustomNavBarSubtitlePreferenceKey: PreferenceKey{
    static var defaultValue: String? = nil
    
    static func reduce(value: inout String?, nextValue: () -> String?) {
        value = nextValue()
    }
}

/// 导航栏隐藏返回按钮偏好设置
struct CustomNavBarBackButtonHiddenPreferenceKey: PreferenceKey{
    static var defaultValue: Bool = false
    
    static func reduce(value: inout Bool, nextValue: () -> Bool) {
        value = nextValue()
    }
}

/// 扩展 View
extension View{
    /// 保存导航栏标题
    func customNavigationTitle(_ title: String) -> some View{
        preference(key: CustomNavBarTitlePreferenceKey.self, value: title)
    }
    
    /// 保存导航栏子标题
    func customNavigationSubtitle(_ subtitle: String?) -> some View{
        preference(key: CustomNavBarSubtitlePreferenceKey.self, value: subtitle)
    }
    
    /// 保存导航栏是否显示回退键
    func customNavigationBarBackButtonHidden(_ value: Bool) -> some View{
        preference(key: CustomNavBarBackButtonHiddenPreferenceKey.self, value: value)
    }
    
    /// 自定义导航栏选项
    func customNavBarItems(title: String = "", subtitle: String? = nil, backButtonHidden: Bool = false) -> some View{
        self
            .customNavigationTitle(title)
            .customNavigationSubtitle(subtitle)
            .customNavigationBarBackButtonHidden(backButtonHidden)
    }
}

2. 创建自定义导航栏视图 CustomNavBarView.swift

Swift 复制代码
import SwiftUI

/// 自定义导航栏视图
struct CustomNavBarView: View {
    @Environment(\.presentationMode) var presentationMode
    let showBackButton: Bool
    let title: String //""
    let subtitle: String? //nil
    
    var body: some View {
        HStack {
            if showBackButton {
                backButton
            }
            Spacer()
            titleSection
            Spacer()
            if showBackButton {
                backButton
                    .opacity(0)
            }
        }
        .padding()
        .accentColor(.white)
        .foregroundColor(.white)
        .font(.headline)
        .background(
            Color.accentColor.ignoresSafeArea(edges: .top)
        )
    }
}

extension CustomNavBarView{
    /// 返回按钮
    private var backButton: some View{
        Button {
            presentationMode.wrappedValue.dismiss()
        } label: {
            Image(systemName: "chevron.left")
                .padding()
        }
    }
    
    /// 标题视图
    private var titleSection: some View{
        VStack(spacing: 4) {
            Text(title)
                .font(.title)
                .fontWeight(.semibold)
            if let subtitle = subtitle{
                Text(subtitle)
            }
        }
    }
}

struct CustomNavBarView_Previews: PreviewProvider {
    static var previews: some View {
        VStack {
            CustomNavBarView(showBackButton: true, title: "Title", subtitle: "Subtitle")
            Spacer()
        }
    }
}

3. 创建自定义导航栏容器视图 CustomNavBarContainerView.swift

Swift 复制代码
import SwiftUI

/// 自定义导航栏容器视图
struct CustomNavBarContainerView<Context: View>: View {
    let context: Context
    @State private var showBackButton: Bool = true
    @State private var title: String = ""
    @State private var subtitle: String? = nil
    init(@ViewBuilder context: () -> Context) {
        self.context = context()
    }
    
    var body: some View {
        VStack(spacing: 0) {
            CustomNavBarView(showBackButton: showBackButton, title: title, subtitle: subtitle)
            context
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
         // 监听偏好值
        .onPreferenceChange(CustomNavBarTitlePreferenceKey.self) { value in
            self.title = value
        }
        .onPreferenceChange(CustomNavBarSubtitlePreferenceKey.self) { value in
            self.subtitle = value
        }
        .onPreferenceChange(CustomNavBarBackButtonHiddenPreferenceKey.self) { value in
            self.showBackButton = !value
        }
    }
}

struct CustomNavBarContainerView_Previews: PreviewProvider {
    static var previews: some View {
        CustomNavBarContainerView {
            ZStack {
                Color.green.ignoresSafeArea()
                Text("Hello world")
                    .foregroundColor(.white)
                    .customNavigationTitle("Title")
                    .customNavigationSubtitle("Subtitle")
                    .customNavigationBarBackButtonHidden(true)
            }
        }
    }
}

4. 创建自定义导航视图 CustomNavView.swift

Swift 复制代码
import SwiftUI

/// 自定义导航视图
struct CustomNavView<Content: View>: View {
    /// 泛型
    let context: Content
    
    init(@ViewBuilder context: () -> Content) {
        self.context = context()
    }
    
    var body: some View {
        NavigationView {
            CustomNavBarContainerView {
                context
            }
        }
        .navigationViewStyle(.stack)
    }
}

extension UINavigationController{
    open override func viewDidLoad() {
        super.viewDidLoad()
        // 手势识别器交互代理置为 nil,进入下一个导航页面,从左往右滑,能够移除当前页面
       interactivePopGestureRecognizer?.delegate = nil
    }
}

struct CustomNavView_Previews: PreviewProvider {
    static var previews: some View {
        CustomNavView {
            Color.red.ignoresSafeArea()
        }
    }
}

5. 创建自定义导航视图链接到下一个视图 CustomNavLink.swift

Swift 复制代码
import SwiftUI

/// 自定义导航视图链接下个视图
struct CustomNavLink<Label: View, Destination: View>: View {
    
    let destination: Destination
    let lable: Label
    
    init(@ViewBuilder destination: () -> Destination, @ViewBuilder label: () -> Label) {
        self.destination = destination()
        self.lable = label()
    }
    
    var body: some View {
        NavigationLink {
            CustomNavBarContainerView {
                destination
            }
            .navigationBarHidden(true)
            
        } label: {
            lable
        }
    }
}

struct CustomNavLink_Previews: PreviewProvider {
    static var previews: some View {
        // 自定义导航试图
        CustomNavView {
            CustomNavLink {
                Text("Destination")
            } label: {
                Text("CLICK ME")
            }
        }
    }
}

6. 创建应用导航栏视图 AppNavBarView.swift

Swift 复制代码
import SwiftUI

/// 应用导航栏视图
struct AppNavBarView: View {
    var body: some View {
        /// 系统默认导航栏
        //defaultNavBavView
        /// 自定义导航栏
        customNavBavView
    }
}

/// 扩展 View
extension AppNavBarView{
    /// 系统默认导航栏
    private var defaultNavBavView: some View{
        NavigationView {
            ZStack {
                Color.green
                    .ignoresSafeArea()
                NavigationLink {
                    Text("Destination")
                        .navigationTitle("Title2")
                        .navigationBarBackButtonHidden(false)
                } label: {
                    Text("Navigate")
                        .foregroundColor(.black)
                }
            }
            .navigationTitle("Nav title here")
        }
    }
    
    /// 自定义导航栏
    private var customNavBavView: some View{
        CustomNavView {
            ZStack {
                Color.orange.ignoresSafeArea(edges: .bottom)
                CustomNavLink {
                    Text("Destination")
                        .customNavBarItems(title: "Second Screen", subtitle: "Subtitle should be showing!!!")
                } label: {
                    Text("Navigate")
                }
            }
            .customNavBarItems(title: "New Title", subtitle: "Subtitle", backButtonHidden: true)
        }
    }
}

struct AppNavBarView_Previews: PreviewProvider {
    static var previews: some View {
        AppNavBarView()
    }
}

7. 效果图:

相关推荐
__WanG1 小时前
如何申请 Family Controls 权限
ios·swiftui·权限·应用时间管理
清水迎朝阳3 小时前
Qt 小白成长系列 2 QUiLoader 动态加载UI
ui
sandwu6 小时前
AI自动化测试(二)—— Playwright-MCP搭建自动化UI测试(browser-use&midscene对比)
人工智能·ui·自动化·playwright
weixin_439706259 小时前
flowable 6.8 + flowable ui + spring boot的例子
spring boot·后端·ui
五月君_1 天前
Nuxt UI v4.3 发布:原生 AI 富文本编辑器来了,Vue 生态又添一员猛将!
前端·javascript·vue.js·人工智能·ui
深蓝海拓1 天前
PySide6从0开始学习的笔记(二十一) 使用loadUi直接加载.ui文件
笔记·python·qt·学习·ui·pyqt
工业HMI实战笔记1 天前
HMI多任务操作设计:如何避免多设备监控时的注意力分散?
ui·信息可视化·人机交互·交互·ux
航Hang*1 天前
Photoshop 图形与图像处理技术——第6章:通道与蒙版的应用
图像处理·笔记·ui·photoshop
杭城小刘1 天前
Weex 底层原理与 APM
ios·前端框架·weex
2501_924064111 天前
2025年移动应用渗透测试流程方案及iOS安卓测试方法对比
android·ios