Search code examples
iphoneswiftuiipadtabsrendering

SwiftUI TabItem format on iPad vs iPhone?


I have an iPhone app which I have configured to also run on an iPad. It works well on both iPhone and iPad, except for the fact that the TabView "tab" items are rendered aestetically differently on the iPad vs the iPhone. I would like to make the iPad tab look the same as on the iPhone but I can't seem to figure out how to do that! Any help would be appreciated..!

Using Xcode 14.3.1

Target OS: iOS 16.2

Deployment info on Xcode:

enter image description here

My TabView code looks like this:

struct ContentView: View {
    
    @EnvironmentObject var BLEinfo: BLEdata
    
    // let timer = Timer.publish (every: 0.8, on: .current, in: .common).autoconnect()
    
    var body: some View {
        
        HStack {
            if UIDevice().model == "iPad" && !self.BLEinfo.iPadWiderView {
                Spacer().background(.black)
            }
            TabView(selection: self.$BLEinfo.tabSelected) {
                
                VStack(spacing: 0) {
                    configTabView()
                }
                .tabItem {
                    VStack(spacing: 0) {
                        Image(systemName: "rectangle.compress.vertical")
                            .font(Font.system(.title ))
                        Text("Config. Meter")
                    }
                } .tag(0)
                
                VStack(spacing: 0) {
                    settingsTabView()
                    statusLineView()
                }
                .tabItem {
                    VStack(spacing: 0) {
                        Image(systemName: "gear.badge.questionmark")
                            .font(Font.system(.title ))
                        Text("Settings & About")
                    }
                } .tag(1)
                
                VStack(spacing: 0) {
                    connectTabView()
                    statusLineView()
                    // Spacer()
                }
                .tabItem {
                    VStack(spacing: 0) {
                        Image(systemName: "dot.radiowaves.left.and.right" )
                            .font(Font.system(.title ))
                        Text("Connect Meter")
                    }
                } .tag(2)
                
                VStack(spacing: 0) {
                    readMeterTabView()
                    statusLineView()
                }
                .tabItem {
                    // Label("Read Meter", systemImage: "speedometer")
                    VStack(spacing: 0) {
                        Image(systemName: "speedometer")
                            .font(Font.system(.title ))
                        Text("Read Meter")
                    }
                } .tag(3)
            }.font(.system(size: CGFloat(self.BLEinfo.nonDynamicUIfontSize)))
                .accentColor(.gray)
        }.background(Color.init(red: 30/255, green: 30/255, blue: 30/255))
    }
}

On an iPhone it renders like this (screenshot of bottom half of iPhone (portrait) screen:

enter image description here

On an iPad it renders like this (screenshot of bottom half of iPad (portrait) screen:

enter image description here

Note how the iPad rendering of the TabItems basically ignores the "VStack" for each TabItem (so it renders the whole tabItem area as an "HStack".

How do I get the same "look" on an iPad? In other words the "Text" of the TabItem needs to be placed BELOW the "image" of the TabItem?

Edit: I have already tried to remove the "VStack" from each "tabItem". That makes no difference on both iPhone & iPad.

Also tried to use the shorter/newer "label" format for the tabItems, so using:

Label("Read Meter", systemImage: "speedometer")

Also has no effect on either device!

Thanks!


Solution

  • According to this post (which is about the same problem but for UIKit), one way to do this is to override the horizontal size class to .compact. I'm not sure if this is guaranteed to work in future versions, because the tab items displaying horizontally is by-design (See this particular answer). You might want to create your own tab bar instead.

    You can override the size class in SwiftUI by modifying the TabView with:

    .environment(\.horizontalSizeClass, .compact)
    

    Note that this would also affect the views in each tab, if they depend on the horizontal size class. To avoid that, get what the size class should have been using an @Environment and pass that to the tabs.

    Here's a rough sketch:

    @Environment(\.horizontalSizeClass) var oldSizeClass
    
    var body: some View {
        TabView {
            SomeView()
                .tabItem {
                    Label(...)
                }
                .environment(\.horizontalSizeClass, oldSizeClass)
            
            AnotherView()
                .tabItem {
                    Label(...)
                }
                .environment(\.horizontalSizeClass, oldSizeClass)
        }.environment(\.horizontalSizeClass, .compact)
    }