CustomNavBar 自定义导航栏视图

1. 创建偏好设置键 CustomNavBarTitlePreferenceKey.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

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

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

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

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

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

        

猜你喜欢

转载自blog.csdn.net/u011193452/article/details/133815852