3

I am trying to see if I can make the color of the bottom tabview change depending on which tab item is selected. Currently I can make the tabview bar clear with the below code in the init.

let tabBar = UITabBar.appearance()
    init() {
        tabBar.barTintColor = UIColor.clear
        tabBar.backgroundImage = UIImage()
        tabBar.shadowImage = UIImage()
    }
 ...
 TabView(selection: $selectedTab) {
                FirstView()
                    .tabItem{
                        Text("First")
                    }
                SecondView()
                    .tabItem{
                        Text("Second")
                    }
    }
    .onAppear{
setTabViewBackground()
}

func setTabViewBackground() {
        if selectedTab != 0 {
            tabBar.barTintColor = UIColor.blue
        }
    }

Tried to just fire the func when the body redraws and idk if its this declarative style that's getting the best of me but doesn't change the tabview background at all.

Kenji
  • 33
  • 1
  • 4
  • You can extend UITabBarController to get this to work. About halfway down this page is a reference on how to do it. Look for the code after "The New Way: UITabBarAppearance", https://schwiftyui.com/swiftui/customizing-your-tabviews-bar-in-swiftui/ – Dan O'Leary Aug 14 '20 at 01:37
  • That article was what I started with but was getting strange results with Xcode 12. I ended up just making my own tabview as an hstack of buttons that change a binding that conditionally fires the 4 other views. Its way less code than what's in the article but was wondering if there is any real disadvantage. – Kenji Aug 25 '20 at 17:26

1 Answers1

4

Any .appearance affects instances created after the appearance itself. So the solution is to re-create TabView after appearance configuration changed.

Here is a demo of approach. Tested with Xcode 12 / iOS 14.

demo

struct DemoView: View {

    let tabBar = UITabBar.appearance()

    init() {
        tabBar.barTintColor = UIColor.red
    }

    @State private var selectedTab = 0    // preserves selected tab

    var body: some View {
        TabView(selection: $selectedTab) {
            Text("FirstView")
                .tabItem{
                    Text("First")
                }.tag(0)
            Text("SecondView")
                .tabItem{
                    Text("Second")
                }.tag(1)
        }
        .onAppear {
            setTabViewBackground()  // called, because `id` is below
        }
        .id(selectedTab)   // recreates entire above view with new tabbar
    }

    func setTabViewBackground() {
        tabBar.barTintColor = selectedTab != 0 ? .blue : .red
    }
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • @Asperi that works but it seems to mess up the tab components themselves. they don't load properly with this – Timmerz Sep 23 '20 at 01:43
  • This solution would reset scroll position and any other state. Not what you expect from a tab view. Please add this information to your answer. – Phil Dukhov Sep 24 '21 at 07:11