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 tab bar and use a custom one. Then you can style it any way you like.

    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) {
                            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() }
        }
    }
    

    The custom tab bar can be attached to the main content using .safeAreaInset(edge: .bottom).

    struct ContentView: View {
        @State private var selection = TabType.heart
    
        var body: some View {
            TabView(selection: $selection) {
                Text("1")
                    .tag(TabType.heart)
                    .toolbarVisibility(.hidden, for: .tabBar)
                    // pre iOS 18: .toolbar(.hidden, for: .tabBar)
                Text("2")
                    .tag(TabType.settings)
                    .toolbarVisibility(.hidden, for: .tabBar)
            }
            .safeAreaInset(edge: .bottom) {
                CustomTabBar(selection: $selection)
            }
        }
    }
    
    ZStack {
        switch selection {
        case .heart:
            Text("1")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .transition(.move(edge: .leading))
        case .settings:
            Text("2")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .transition(.move(edge: .trailing))
        }
    }
    .safeAreaInset(edge: .bottom) {
        CustomTabBar(selection: $selection)
    }
    

    Animation

    If you want to use a sliding transition for three or more tabs, you might find the answer to SwiftUI bi-directional move transition moving the wrong way in certain cases useful.