Search code examples
swiftswiftuitabbar

SwiftUi Change Tint of Unselected Item in Tab Bar


I was trying to change the tint color of an unselected item in SwiftUI.

Currently, I got it so it has a background and changes the tint color (making the tab bar visible in .tabItem gets rid of the .red tint and makes it gray)

import SwiftUI
import UIKit

struct Test: View{
    
    init(){
        UITabBar.appearance().backgroundColor = UIColor.systemBackground
        UITabBar.appearance().unselectedItemTintColor = .red
    }
    
    var body: some View{
        
        TabView{
            Text("1")
                .tabItem{
                    Image(systemName: "heart.fill")
                    Text("Heart")
                }
              
                
            Text("2")
                .tabItem{
                    Image(systemName: "gearshape.fill")
                    Text("Settings")
                }
        }
    }
}

#Preview{
    Test()
}

Image 1

Now I'm trying to add the divider line that separates the tab bar with the rest of the stuff on the page. Doing .toolbarBackground(.visible, for: .tabBar) brings back the divider, but then the unselected item tint doesn't work anymore.

TabView{
            Text("1")
                .tabItem{
                    Image(systemName: "heart.fill")
                    Text("Heart")
                }
                .toolbarBackground(.visible, for: .tabBar)

enter image description here

I also tried doing this method but it seems like it doesn't work anymore.


Solution

  • I would just abandon the native TabView and use a custom tab bar. Then you can style it any way you like.

    • It works well to use an enum to define the tab types.
    • The custom tab bar then has one button per enum type.
    • A custom LabelStyle can be used to style the labels of the buttons.

    Something like:

    enum TabType: CaseIterable {
        case heart
        case settings
    
        var titleKey: String {
            "\(self)".capitalized
        }
    
        var symbolName: String {
            switch self {
            case .heart: "heart"
            case .settings: "gearshape"
            }
        }
    }
    
    struct TabLabelStyle: LabelStyle {
        func makeBody(configuration: Configuration) -> some View {
            VStack(spacing: 4) {
                configuration.icon
                    .dynamicTypeSize(.xxxLarge)
                configuration.title
                    .font(.caption)
            }
        }
    }
    
    struct CustomTabBar: View {
        @Binding var selection: TabType
    
        var body: some View {
            HStack {
                ForEach(TabType.allCases, id: \.self) { type in
                    Button {
                        withAnimation(.spring(duration: 0.2)) {
                            selection = type
                        }
                    } label: {
                        Label(type.titleKey, systemImage: type.symbolName)
                            .labelStyle(TabLabelStyle())
                            .symbolVariant(selection == type ? .fill : .none)
                            .foregroundStyle(selection == type ? Color.accentColor : .red)
                            .frame(maxWidth: .infinity)
                    }
                }
            }
            .padding()
            .background(.bar)
            .overlay(alignment: .top) { Divider() }
        }
    }
    
    struct ContentView: View {
        @State private var selection = TabType.heart
    
        var body: some View {
            VStack(spacing: 0) {
                ZStack {
                    switch selection {
                    case .heart: Text("1")
                    case .settings: Text("2")
                    }
                }
                .frame(maxWidth: .infinity, maxHeight: .infinity)
    
                CustomTabBar(selection: $selection)
            }
        }
    }
    

    Animation