Making TabView not translucent on SwiftUI produces a new view on top
Asked Answered
P

4

17

Hello everyone. I'm creating a simple SwiftUI app and I'd like my app's TabView to have a custom background and not be translucent.

To achieve this I use UITabBar.appearance().backgroundColor = Color and UITabBar.appearance().isTranslucent = false, which is supposed to do exactly that, and yes, it makes the bar not translucent, but instead of giving the bar the color I chose, it produces a new view on top of the tab bar that isn't supposed to be there, and obviously wasn't there before.

Without changing tab bar translucency and color without changing tab bar color

Changing tab bar translucency and color changing tab bar color

You can notice the new view that appeared. I guess this is a problem with isTranslucent because when I remove it the new view is gone.

Is there a way I can change the color and make the bar not translucent and not having that view appearing?

Any help is appreciated. Thanks in advance.

My code

SceneDelegate (only the changing color part)

UITabBar.appearance().isTranslucent = false
UITabBar.appearance().backgroundColor = UIColor(named: "backgroundColor")

TabView

struct TabController: View {
    @State private var selection = 0

    var body: some View {
        TabView(selection: $selection) {
            HomePageView()
                .tabItem {
                    Image(systemName: "house.fill")
                        .font(.title)
                }
                .tag(0)

            Text("Second View")
                .font(.title)
                .tabItem {
                    Image(systemName: "bell.fill")
                        .font(.title)
                }
                .tag(1)
        }
            .edgesIgnoringSafeArea(.top)
    }
}
Particularize answered 7/9, 2019 at 8:50 Comment(1)
By inspection, the gray area is not an additional view, it is actually the background. The foreground views somehow got squeezed upwards by exactly an extra height of the tab bar (83px), revealing the gray background.Breast
P
8

This is the correct way to do it.

It works with SwiftUI too as the TabView and NavigationView are actually UIHostedController for the legacy UITabBarController and UINavigationController.

Edit: Just watched Modernizing Your UI for iOS 13 This is the way to do it :

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor  .white]

Then set the appearance on the various type of appearance.

navigationBar.standardAppearance = appearance
navigationBar.compactAppearance = appearance
navigationBar.scrollEdgeAppearance = appearance

Reference: https://developer.apple.com/videos/play/wwdc2019/224/

2nd Edit: Need a figure out a clean way to get to the UINavigationController from a SwiftUI view.

In the meantime, this will help:

extension UINavigationController {
    override open func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        navigationBar.standardAppearance = appearance
        navigationBar.compactAppearance = appearance
        navigationBar.scrollEdgeAppearance = appearance
    }
}

extension UITabBarController {
    override open func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let appearance = UITabBarAppearance()
        appearance.configureWithOpaqueBackground()
        tabBar.standardAppearance = appearance
    }
}
Pick answered 1/10, 2019 at 16:9 Comment(3)
Thanks, but I can't use this for TabView. Do you know if it is possible? Thanks for answering anyways.Particularize
@iAlex11 ... edited my answer. There should be a cleaner way to get to the navigation controller.Pick
@Pick You can also create an appearance object and set the UINavigationBar.appearance().standardAppearance = newAppearanceShrader
F
20

You can set a tabbar color with this code. Write this code in SceneDelegate

UITabBar.appearance().shadowImage = UIImage()
UITabBar.appearance().backgroundImage = UIImage()
UITabBar.appearance().isTranslucent = true
UITabBar.appearance().backgroundColor = .black

In TabBar Background you can set any other color instead of black. Its working perfectly fine.

ContentView:

TabView(selection: $selection) {
        Text("1st View")
            .tabItem {
                Image(systemName: "house.fill")
                    .font(.title)
            }
            .tag(0)

        Text("Second View")
            .font(.title)
            .tabItem {
                Image(systemName: "bell.fill")
                    .font(.title)
            }
            .tag(1)
    }
        .edgesIgnoringSafeArea(.top)

Preview:

Forthright answered 4/10, 2019 at 7:31 Comment(0)
P
8

This is the correct way to do it.

It works with SwiftUI too as the TabView and NavigationView are actually UIHostedController for the legacy UITabBarController and UINavigationController.

Edit: Just watched Modernizing Your UI for iOS 13 This is the way to do it :

let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor  .white]

Then set the appearance on the various type of appearance.

navigationBar.standardAppearance = appearance
navigationBar.compactAppearance = appearance
navigationBar.scrollEdgeAppearance = appearance

Reference: https://developer.apple.com/videos/play/wwdc2019/224/

2nd Edit: Need a figure out a clean way to get to the UINavigationController from a SwiftUI view.

In the meantime, this will help:

extension UINavigationController {
    override open func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let appearance = UINavigationBarAppearance()
        appearance.configureWithOpaqueBackground()
        navigationBar.standardAppearance = appearance
        navigationBar.compactAppearance = appearance
        navigationBar.scrollEdgeAppearance = appearance
    }
}

extension UITabBarController {
    override open func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        let appearance = UITabBarAppearance()
        appearance.configureWithOpaqueBackground()
        tabBar.standardAppearance = appearance
    }
}
Pick answered 1/10, 2019 at 16:9 Comment(3)
Thanks, but I can't use this for TabView. Do you know if it is possible? Thanks for answering anyways.Particularize
@iAlex11 ... edited my answer. There should be a cleaner way to get to the navigation controller.Pick
@Pick You can also create an appearance object and set the UINavigationBar.appearance().standardAppearance = newAppearanceShrader
B
2

You can access UINavigationController or UITabbarController to use [https://github.com/siteline/SwiftUI-Introspect]

and write like this

NavigationVIew {...}
.introspectNavigationController { (navController) in
    let coloredAppearance = UINavigationBarAppearance()
    coloredAppearance.configureWithOpaqueBackground()
    
    coloredAppearance.backgroundColor = backgroundColor
    coloredAppearance.backgroundImage?.withTintColor(backgroundColor)
    coloredAppearance.titleTextAttributes = [.foregroundColor: tintColor]
    coloredAppearance.largeTitleTextAttributes = [.foregroundColor: tintColor]
    coloredAppearance.shadowColor = shadowColor
    
    navController.navigationBar.compactAppearance = coloredAppearance
    navController.navigationBar.standardAppearance = coloredAppearance
    navController.navigationBar.scrollEdgeAppearance = coloredAppearance
    navController.navigationBar.tintColor =  tintColor
}
Brandon answered 7/1, 2021 at 5:50 Comment(0)
K
2

You can use an extension like this:

extension UITabBarController {
    override open func viewDidLoad() {
        super.viewDidLoad()
        let appearance = UITabBarAppearance()
        appearance.backgroundColor = .black
        tabBar.standardAppearance = appearance
    }
}

Pay attention that the overridden function must be viewDidLoad(). At least it doesn't work for me when it is a viewDidAppear(:) function.

Karisakarissa answered 9/3, 2021 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.