Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 949
Sheehan Munim Avatar asked Oct 30 '25 10:10

Sheehan Munim


1 Answers

I would just abandon the native tab bar and use a custom one. 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 {
    let isSelected: Bool

    func makeBody(configuration: Configuration) -> some View {
        VStack(spacing: 4) {
            configuration.icon
                .imageScale(.large)
            configuration.title
                .font(.caption)
        }
        .symbolVariant(isSelected ? .fill : .none)
        .foregroundStyle(isSelected ? Color.accentColor : .red)
        .frame(maxWidth: .infinity)
    }
}

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(isSelected: selection == type))
                }
            }
        }
        .padding()
        .background(.bar)
        .overlay(alignment: .top) { Divider() }
    }
}

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

  • For the main content itself, you can still use a TabView if you like. This might have some advantages in terms of preserving state (such as, preserving scroll position). When doing it this way, the native tab bar must be hidden for every child view:
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)
        }
    }
}
  • Alternatively, you can perform your own switching, using a container such as a ZStack. This allows you to determine the transition when switching:
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.

like image 145
Benzy Neez Avatar answered Nov 01 '25 14:11

Benzy Neez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!