Custom TabBar SwiftUI

let’s start with the Tabbar. For each tab bar item, we need a title, image, and selected image. So we need to create a struct name TabItemData.

struct TabItemData {
    let image: String
    let selectedImage: String
    let title: String
}

Then let prepare the TabItemView, which is one of three items in the Tabbar. We have an image and text center vertical, so let wrap it into a VStack. Also, it has two states one is selected and one is un-selected. The input of this TabItemView should be TabItemData and isSelected.

struct TabItemView: View {
    let data: TabItemData
    let isSelected: Bool
    
    var body: some View {
        VStack {
            Image(isSelected ? data.selectedImage : data.image)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 32, height: 32)
                .animation(.default)
            
            Spacer().frame(height: 4)
            
            Text(data.title)
                .foregroundColor(isSelected ? .black : .gray)
                .font(.system(size: 14))
        }
    }
}

Next, We will build the custom Tabbar, we will name it TabBottomView . In this design, it has three items, horizontally center, so we will wrap them into an HStack, round all corners, and add a white background with a smooth shadow effect.

struct TabBottomView: View {
    
    let tabbarItems: [TabItemData]
    var height: CGFloat = 70
    var width: CGFloat = UIScreen.main.bounds.width - 32
    @Binding var selectedIndex: Int
    
    var body: some View {
        HStack {
            Spacer()
            
            ForEach(tabbarItems.indices) { index in
                let item = tabbarItems[index]
                Button {
                    self.selectedIndex = index
                } label: {
                    let isSelected = selectedIndex == index
                    TabItemView(data: item, isSelected: isSelected)
                }
                Spacer()
            }
        }
        .frame(width: width, height: height)
        .background(Color.white)
        .cornerRadius(13)
        .shadow(radius: 5, x: 0, y: 4)
    }
}

In this view, we will allow change width and height from outside. So the input should be tabbarItemswidthheight , and binding variable selectedIndex . We will wrap the TabItemView into a Button because we need to handle it when users touch in, we will update the selectedIndex .

struct CustomTabView<Content: View>: View {
    
    let tabs: [TabItemData]
    @Binding var selectedIndex: Int
    @ViewBuilder let content: (Int) -> Content
    
    var body: some View {
        ZStack {
            TabView(selection: $selectedIndex) {
                ForEach(tabs.indices) { index in
                    content(index)
                        .tag(index)
                }
            }
            
            VStack {
                Spacer()
                TabBottomView(tabbarItems: tabs, selectedIndex: $selectedIndex)
            }
            .padding(.bottom, 8)
        }
    }
}
enum TabType: Int, CaseIterable {
    case home = 0
    case myFile
    case profile
    
    var tabItem: TabItemData {
        switch self {
        case .home:
            return TabItemData(image: "ic_home", selectedImage: "ic_home_selected", title: "Home")
        case .myFile:
            return TabItemData(image: "ic_myfile", selectedImage: "ic_myfile_selected", title: "My File")
        case .profile:
            return TabItemData(image: "ic_profile", selectedImage: "ic_profile_selected", title: "Me")
        }
    }
}
struct MainTabView: View {
    
    @State var selectedIndex: Int = 0
    
    var body: some View {
        CustomTabView(tabs: TabType.allCases.map({ $0.tabItem }), selection: $selectedIndex) { index in
            let type = TabType(rawValue: index) ?? .home
            getTabView(type: type)
        }
    }
    
    @ViewBuilder
    func getTabView(type: TabType) -> some View {
        switch type {
        case .home:
            HomeView()
        case .myFile:
            MyFileView()
        case .profile:
            ProfileView()
        }
    }
}

Output:

 

 

References: Truong Van Klen

GithubRepo: https://github.com/Joynal279/TabBarDemoApp

693 thoughts on “Custom TabBar SwiftUI

Leave a Reply