Search code examples
swiftuibackground

How can I give my TabBar the same background in Swiftui?


my views all have a background (LinearGradien) but my CustomTabBar is white and then it all looks very cut off, to get around this I wanted to set the background to .clear but it stayed white. So my question is how to make it so that my tabBar is still there but takes on the background of the views.

Many thanks in advance

ContentView

struct ContentView: View {
    @State private var selectedTab: Tab = .house
    @State private var isProjectScreen: Bool = SharedDataIsProjectScreen.sharedVariable
    
    init() {
        UITabBar.appearance().isHidden = true
    }
    
    @State var isFullView:Bool = false
    @State private var showSignInView: Bool = false
    
    var body: some View {
        VStack{
            if !showSignInView {
                
                    VStack {
                        VStack {
                            TabView(selection: $selectedTab) {
                                dashboard_iphone_view(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                                    .tag(Tab.house)
                                
                                todo_iphone_view(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                                    .tag(Tab.list)
                                
                                pomodoro_timer_iphone(isFullView: $isFullView, profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                                    .tag(Tab.clock)
                                
                                Listen_View(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                                    .tag(Tab.listen)
                            }
                        }
                        
                        if isFullView == false{
                            CustomTabBar(selectedTab: $selectedTab)
                        }
                    }
            }
        }
        .statusBarHidden(isFullView)
    }
}

CustomTabBar

import SwiftUI

enum Tab: String, CaseIterable {
    case house = "house"
    case list = "list.bullet.clipboard"
    case clock = "clock"
    //case folder = "folder"
    case listen = "list.clipboard"
}


struct CustomTabBar: View {
    @State var progress: Double = 0.50
    @Binding var selectedTab: Tab
    private var fillImage: String {
        selectedTab.rawValue + ".fill"
    }
    
    private var tabColor: Color{
        switch selectedTab {
        case .house:
            return .white
        case .list:
            return .white
        case .clock:
            return .white
        //case .folder:
          //  return .marineblau
        case .listen:
            return .white
        }
    }
    var body: some View {
        VStack{
            HStack{
                ForEach(Tab.allCases, id: \.rawValue){ tab in
                    Spacer()
                    Image(systemName: selectedTab == tab ? fillImage : tab.rawValue)
                        .scaleEffect(selectedTab == tab ? 1.25 : 1.0)
                        .foregroundStyle(selectedTab == tab ? tabColor : Color(hex: "E8E8E8"))
                        .font(.system(size: 22))
                        .onTapGesture{
                            withAnimation(.easeIn(duration: 0.1)){
                                selectedTab = tab
                            }
                        }
                    Spacer()
                    
                }
            }
            
            
        }
        .frame(width: nil, height: 60)
        .background(.clear)
    }  
   
}```

Solution

  • You currently have a VStack which contains the TabView at the top and CustomTabBar at the bottom. So it is no surprise, that the background of the child views do not extend behind the tab bar.

    Unfortunately, a fix is not just a simple matter of moving the background to the VStack, because each view inside a TabView is hosted by a "container view" with a white background. If the full-screen background would be a solid color, or an image, then you could show it behind each child view and also behind the VStack and you might not notice where the join is, but this doesn't work for a gradient (due to the different heights).

    So to fix, try these changes:

    • Instead of using a VStack, show the CustomTabBar as an overlay over the TabView with alignment: .bottom.
    • Each child view should use the full height of the screen, with the gradient behind it. Then use padding to reserve space for the tab bar below each child view. An easy way to implement this is to use a ZStack as the container for each child view.
    • You have fixed the height of CustomTabBar to 60pt, so this is the size of the bottom padding that each child needs to have.
    • It is important that .ignoresSafeArea() is applied to either the linear gradient, or to the container of each child view, otherwise the views might fail to "stick" to the bottom of the screen when switching between tabs.
    • Some of the VStack in your ContentView are redundant, you can remove them.
    • Also, instead of configuring UITabBar.appearance in init, add .toolbar(.hidden, for: .tabBar) to each child view. For good measure, it is probably a good idea to add .toolbarBackground(.hidden, for: .tabBar) too.

    Here is an adapted version of your example to show it working:

    struct ContentView: View {
        @State private var selectedTab: Tab = .house
        @State private var isProjectScreen: Bool = false //SharedDataIsProjectScreen.sharedVariable
    
    //    init() {
    //        UITabBar.appearance().isHidden = true
    //    }
    
        @State var isFullView:Bool = false
        @State private var showSignInView: Bool = false
    
        private var linearGradient: LinearGradient {
            LinearGradient(
                colors: [.yellow, .red],
                startPoint: .top,
                endPoint: .bottom
            )
        }
    
        private var dashboard_iphone_view: some View {
            ZStack {
                linearGradient
                    .ignoresSafeArea()
                Text("dashboard_iphone_view")
                    .padding(.bottom, 60)
            }
        }
    
        private var todo_iphone_view: some View {
            ZStack {
                linearGradient
                    .ignoresSafeArea()
                Text("todo_iphone_view")
                    .padding(.bottom, 60)
            }
        }
    
        private var pomodoro_timer_iphone: some View {
            ZStack {
                linearGradient
                    .ignoresSafeArea()
                Text("pomodoro_timer_iphone")
                    .padding(.bottom, 60)
            }
        }
    
        private var Listen_View: some View {
            ZStack {
                linearGradient
                    .ignoresSafeArea()
                Text("Listen_View")
                    .padding(.bottom, 60)
            }
        }
    
        var body: some View {
            VStack{
                if !showSignInView {
                    TabView(selection: $selectedTab) {
                        dashboard_iphone_view
    //                    dashboard_iphone_view(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                            .toolbar(.hidden, for: .tabBar)
                            .toolbarBackground(.hidden, for: .tabBar)
                            .tag(Tab.house)
    
                        todo_iphone_view
    //                    todo_iphone_view(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                            .toolbar(.hidden, for: .tabBar)
                            .toolbarBackground(.hidden, for: .tabBar)
                            .tag(Tab.list)
    
                        pomodoro_timer_iphone
    //                    pomodoro_timer_iphone(isFullView: $isFullView, profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                            .toolbar(.hidden, for: .tabBar)
                            .toolbarBackground(.hidden, for: .tabBar)
                            .tag(Tab.clock)
    
                        Listen_View
    //                    Listen_View(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                            .toolbar(.hidden, for: .tabBar)
                            .toolbarBackground(.hidden, for: .tabBar)
                            .tag(Tab.listen)
                    }
                    .overlay(alignment: .bottom) {
                        if isFullView == false{
                            CustomTabBar(selectedTab: $selectedTab)
                        }
                    }
                }
            }
            .statusBarHidden(isFullView)
        }
    }
    

    Animation


    EDIT Since you are hiding the default tabbar, there would actually be a much simpler solution:

    • Just use a ZStack instead of a TabView.
    • Use a VStack as the container for the main content (above) and the toolbar (below), as you were doing before.
    • Remove the background from the child views and apply it to the parent VStack instead.
    var body: some View {
        VStack{
            if !showSignInView {
                VStack {
                    ZStack {
                        if selectedTab == .house {
                            dashboard_iphone_view(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                        } else if selectedTab == .list {
                            todo_iphone_view(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                        } else if selectedTab == .clock {
                            pomodoro_timer_iphone(isFullView: $isFullView, profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                        } else if selectedTab == .listen {
                            Listen_View(profilViewModel: ProfilViewModel(), design_selection: Design_selection())
                        }
                    }
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    if isFullView == false{
                        CustomTabBar(selectedTab: $selectedTab)
                    }
                }
                .background(linearGradient)
            }
        }
        .statusBarHidden(isFullView)
    }