Liviu
Liviu

Reputation: 152

Custom NavigationView top bar

enter image description here

I want to create a design similar to the image above. To create the bottom TabView, I used this code:

import SwiftUI

struct ParentTabView: View {

    var body: some View {
    
        TabView {
            HomeView()
                .tabItem {
                    Image(systemName: "star.fill")
                    Text("Home")
                }
            Text("Second Tab")
                .tabItem {
                    Image(systemName: "star.fill")
                    Text("Discover")
                }
            Text("Third Tab")
                .tabItem {
                    Image(systemName: "star.fill")
                    Text("Settings")
                }
        }
    }
}

and the HomeView

struct HomeView: View {
    var body: some View {
        NavigationView {
            Text("Home")
                .navigationBarItems(
                    trailing:
                        Button(action: {
                        }) {
                            Image(systemName: "plus")
                        }
                )
                .navigationTitle("Home")
        }
    }
}

The problem I encounter is with the top navigation bar, as I'm not able to edit the NavigationView to receive other views below the title. I can set the title and add the "plus" button, but not edit the whole top bar.

Can NavigationView be customized in such a way and if not, how can I achieve that result while still keeping the advantages NavigationViews offer, such as NavigationLinks etc.

Upvotes: 1

Views: 195

Answers (1)

Abdulelah Hajjar
Abdulelah Hajjar

Reputation: 207

The NavigationBar provided by Apple is not quite customizable. You might want to create your customized navigation bar for this use case.

Fortunately, I had some time in hand so I created a simple reusable navigation bar that looks like the outcome you wanted, please take a look below.

Custom navigation bar

You have to to hide Apple's navigation bar, and add the custom navigation bar as an overlay to your navigation view (as seen in the image below):

Code to add

This is the code for the reusable custom navigation view:

enum CustomNavigationBarItem: String, CaseIterable {
    case dashboard = "Dashboard"
    case feed = "Feed"
    case followers = "Followers"
}

struct CustomNavigationBar: View {
    var title: String
    var items: [CustomNavigationBarItem]
    @Binding var selectedItem: CustomNavigationBarItem
    var buttonAction: () -> ()

    var body: some View {
        VStack {
            VStack(spacing: 10) {
                CustomNavigationBarHeader(title: title, buttonAction: buttonAction)
                
                HStack(spacing: 0) {
                    ForEach(items, id: \.self) { item in
                        CustomNavigationBarItemView(text: item.rawValue, isSelected: selectedItem == item)
                            .onTapGesture {
                                selectedItem = item
                            }
                    }
                }
            }
            .background(Color(#colorLiteral(red: 0.1036974415, green: 0.1036974415, blue: 0.1036974415, alpha: 1)).edgesIgnoringSafeArea(.all))
            
            Spacer()
        }
    }
}

struct CustomNavigationBarItemView: View {
    var text: String
    var isSelected: Bool
    
    var body: some View {
        Text(text)
            .foregroundColor(isSelected ? .blue : Color(#colorLiteral(red: 0.501960814, green: 0.501960814, blue: 0.501960814, alpha: 1)))
            .frame(maxWidth: .infinity)
            .padding(.vertical, 10)
            .padding(.horizontal, 6)
            .overlay(
                VStack {
                    Spacer()
                    Color.blue
                        .frame(height: 2)
                        .opacity(isSelected ? 1 : 0)
                        .animation(.default)
                }
            )
    }
}

struct CustomNavigationBarHeader: View {
    var title: String
    var buttonAction: () -> ()
    
    var body: some View {
        HStack {
            Text(title)
                .font(.title)
                .bold()
            
            Spacer()
            Button(action: buttonAction, label: {
                Image(systemName: "plus")
            })
            .font(.headline)
        }
        .padding(.horizontal)
    }
}

HomeView Code:

struct HomeView: View {
    @State private var selectedNavigationBarItem: CustomNavigationBarItem = .dashboard
    @State private var numPlusButtonClicked: Int = 0
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Home")
                    
                switch selectedNavigationBarItem {
                    case .dashboard:
                        Text("Currently Showing: Dashboard") //Use your view here
                    case .feed:
                        Text("Currently Showing: Feed") //Use your view here
                    case .followers:
                        Text("Currently Showing: Followers") //Use your views here
                }
                
                Text("Plus Button Clicked \(numPlusButtonClicked) times.")
            }
        }
        .navigationBarHidden(true)
        .overlay(
            CustomNavigationBar(title: "Home",
                                items: [.dashboard, .feed, .followers],
                                selectedItem: $selectedNavigationBarItem) {
                numPlusButtonClicked += 1 // Code executed when the "+" button gets called
            }
        )
    }
}

Notes:

  1. You can add more items to the "CustomNavigationBarItem" enum
  2. By no means this is a perfect component, so feel free to modify it to suit your needs :D

Best of luck!

Edit: Added code for HomeView

Upvotes: 2

Related Questions