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. 效果图:

相关推荐
小雨爱测12 小时前
Photoshop 实战入门:从界面认知到第一张成品图
ui·photoshop
zwd200513 小时前
词频是什么?生词滑块 zipf 3.0 到 5.0 怎么调:同一屏从 0 个生词到 11 个
ios·iphone·效率工具·英语学习·词频·屏幕翻译
螺丝钉tina16 小时前
[Playwright] [02]常用命令与调试:Debug UI、Mode和 Trace Viewer
开发语言·javascript·测试工具·ui·自动化
茶底世界之下16 小时前
灯暖窗冷,色温要不要继续降?用 Awaking 看混合光的取舍
ios
郝学胜-神的一滴18 小时前
游戏设计:原型与开发 18:Unity入门的第一课
人工智能·程序人生·游戏·ui·unity·游戏引擎
2501_9160074721 小时前
Windows OpenSSL 生成 iOS 证书,制作 p12 与描述文件,KXBuild 签名实操
windows·vscode·ios·objective-c·个人开发·swift·敏捷流程
tedcloud1231 天前
Docker Compose 部署 Uptime Kuma:配置 HTTPS、QQ 邮箱告警与数据备份
服务器·网络协议·ui·docker·容器·https·开源
最强小杰1 天前
gpt-image-2.5-sunburst + OpenAI SDK + Cline 怎么接?完整图生图与局部重绘教程
gpt·ios·ai·iphone
kk_net88991 天前
FSNotes:macOS 和 iOS 上的开源笔记管理器
笔记·其他·macos·ios
00后程序员张1 天前
Windows 开发 iOS App 入门,用 SwiftUI 写第一个 iOS App 装到 iPhone
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程