iosswiftxcodeswiftuiios17

SwiftUI TabView adding strange blank space underneath view?


I am using a tab view to change screens in my app. I am also using a custom tab bar that I created to navigate with.

The problem I have is the tab view seems like it's creating a random blank space underneath itself. I can't figure out if it's the custom tab view or not.

IMPORTANT: One thing to note is that if you click anywhere in the blank space it will actually navigate through the views - so it seems like it is actually an invisible tab bar?

Below is a screenshot of the issue:

enter image description here

ContentView:

struct ContentView: View {

@State var selectedTab = 0

var body: some View {
    VStack {
        TabView(selection: $selectedTab) {
            MyGarageView().tag(0)
            FuelView().tag(1)
            FinderView().tag(2)
        }
    }
    ZStack{
        HStack{
            ForEach((TabbedItems.allCases), id: \.self){ item in
                Button{
                    selectedTab = item.rawValue
                } label: {
                    CustomTabItem(imageName: item.iconName, title: item.title, isActive: (selectedTab == item.rawValue))
                }
            }
        }
        .padding(6)
    }
    .frame(height: 75)
    .cornerRadius(15)
    .padding(.horizontal, 26)
  }    

}

CustomTabBarView:

enum TabbedItems: Int, CaseIterable{
    case myGarage = 0
    case fuel
    case map


var title: String{
    switch self {
    case .myGarage:
        return "Garage"
    case .fuel:
        return "Fuel"
    case .map:
        return "Finder"
    }
}

var iconName: String {
    switch self {
    case .myGarage:
        return "garage"
    case .fuel:
        return "fuel"
    case .map:
        return "map"
    }
  }
}

extension ContentView {
func CustomTabItem(imageName: String, title: String, isActive: Bool) -> some View {
    HStack(spacing: 10){
        Spacer()
        Image(imageName)
            .resizable()
            .renderingMode(.template)
            .foregroundColor(isActive ? .white : .gray)
            .frame(width: 20, height: 20)
        if isActive{
            Text(title)
                .font(.system(size: 14))
                .foregroundColor(isActive ? .white : .black)
        }
        Spacer()
    }
    .frame(width: isActive ? 150 : 60, height: 60)
    .background(isActive ? .black : .clear)
    .cornerRadius(15)
  }
}

I've tried hiding the tab view to no avail.

I've also tried playing around with the frames which had no effect.

I cannot see the issue and other posts / articles did not help either. I'm stuck :(


Solution

  • The normal way to use a TabView is either to have a tab bar which is populated with TabItem, or to use page view style.

    You are not using either of these approaches. So there is no real need to be using a TabView.

    However, if you really want to use a TabView, then one workaround for the blank space (when .automatic tab style is used) is to show the custom tab bar as an overlay. You might need to add more padding below the TabView to make more space for your custom tab bar, if the height of your tab bar is greater than the blank space you are trying to hide.

    Like this:

    var body: some View {
        TabView(selection: $selectedTab) {
            MyGarageView().tag(0)
            FuelView().tag(1)
            FinderView().tag(2)
        }
        .padding(.bottom, 30)
        .overlay(alignment: .bottom) {
            HStack{
                ForEach((TabbedItems.allCases), id: \.self){ item in
                    Button{
                        selectedTab = item.rawValue
                    } label: {
                        CustomTabItem(imageName: item.iconName, title: item.title, isActive: (selectedTab == item.rawValue))
                    }
                }
            }
            .padding(6)
            .frame(height: 75)
            .cornerRadius(15)
            .padding(.horizontal, 26)
        }
    }
    

    Screenshot

    Although this works, you are relying on the blank space below the TabView being there, because if it wasn't there, your custom tab bar would overlap the page content. So you might want to reconsider, whether you really need to use a TabView at all.

    An alternative approach would be to replace the TabView with a ZStack and then to switch between the different views according to the setting of the state variable selectedTab. The custom tab bar would work in exactly the same way. By avoiding TabView altogether, you would not be vulnerable to the layout of a TabView changing in a future iOS version.