Richard Kirk
Richard Kirk

Reputation: 331

What is the height of the SwiftUI TabView index with PageTabViewStyle?

I am writing an application using Xcode 14.0.1, and testing on an iPhone 12 mini running iOS 16.0. The current project build is for iOS 14.7. Here is my TabView...

        TabView {
            
            ByEyeView()
                .tabItem { Label("ByEye", systemImage: "eye") }
            
            ChartView()
                .tabItem { Label("Chart", systemImage: "square.grid.4x3.fill") }
            
            ListView()
                .tabItem { Label("List", systemImage: "list.bullet") }
            
            EditView()
                .tabItem { Label("Edit", systemImage: "square.and.pencil") }
            
            CameraView()
                .tabItem { Label("Camera", systemImage:  "camera") }
            
            SettingsView()
                .tabItem { Label("Settings", systemImage: "gear") }
            
        }
        //.labelStyle(TitleAndIconLabelStyle())
        //.padding(8)
        //.ignoresSafeArea(edges: .bottom)
        .tabViewStyle(PageTabViewStyle())
        .indexViewStyle(PageIndexViewStyle(backgroundDisplayMode: .always))

This gives a capsule at the bottom of the page with a small version of the icon and no text. I cannot enlarge the Label with .frame, and the .labelStyle() setting is ignored. I take it this is part of PageTabViewStyle() - the index is supposed to be small, and I can probably not change that. But the index sits over the view content, so I need its height if I am to keep buttons clear of it.

Can I find out the index height? Or does PageTabViewStyle assume that the index is small and you should work around it?

The commented-out .ignoreSafeArea() moves the index down while the page remains the same. The .padding() keeps it a bit clear of the bar at the bottom. This is what I am working with for now. This is foul: it will not work with other devices or screen orientations.

The bigger picture:

I have six entries. That does not fit in the default view, so I get a ... More tag which leads to an extra menu. Ugly. I like the PageTabViewStyle method of scrolling, but I want an index with a known height - preferably one that uses the full labels and sits at the bottom of the TabView layout, under the tabbed views.

Upvotes: 1

Views: 1699

Answers (2)

Richard Kirk
Richard Kirk

Reputation: 331

The other answer is to write your own index table....

           ScrollViewReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack() {
                    pageButton(Page.EyeTest, "eyeglasses", "EyeTest", proxy)
                    pageButton(Page.Tone, "pause.rectangle", "Tone", proxy)
                    pageButton(Page.Chart, "square.grid.4x3.fill", "Chart", proxy)
                    pageButton(Page.ByEye, "eye", "ByEye", proxy)
                    pageButton(Page.List, "list.bullet", "List", proxy)
                    pageButton(Page.Camera, "camera", "Camera", proxy)
                    pageButton(Page.Settings, "gear", "Settings", proxy)
                }
            }
            .onAppear { proxy.scrollTo(page, anchor: .center) }
            .onChange(of: page) { page in
                withAnimation {
                    proxy.scrollTo(page, anchor: .center)
                }
            }
        }

This particular one has button-sized icons and text. 'page' is an enum, and also the tags of the TabView. If you stick it in the layout, you can make it fit around the TabView. You will want to hide the TabView index, which you can do with...

.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))

Upvotes: 0

Richard Kirk
Richard Kirk

Reputation: 331

This was one of those 'Magic Eye' things when you stare at it for days and it makes no sense, and suddenly everything rearranges itself...

Maybe TabPageViewStyle was intended to be for pages where there is no visible index, or overlaying a small index does no harm. This would work for browsing images. All the cunning has gone into making the index view unobtrusive. If you need to know how big it is, then perhaps TabPageViewStyle is not what you want.

What I said I wanted was actually a Scrollable horizontal list, followed by the currently selected list. Something like this...

    let tabW = CGFloat(UIScreen.main.bounds.width / 5.0)
    enum Page {
        case ByEye
        case Chart
        case List
        case Edit
        case Camera
        case Settings
    }
    
    @State private var page = Page.ByEye

    func pageButton(_ select: Page, _ icon: String, _ title: String) -> some View {
        return Button {
            page = select
        } label: {
            VStack {
                Image(systemName: icon)
                Text(title)
            } .frame(width: tabW)
        } .foregroundColor( page == select ? Color.white : Color.gray )
    }
    var body: some View {
        
        VStack() {
            ScrollView(.horizontal) {
                HStack() {
                    pageButton(Page.ByEye, "eye", "ByEye")
                    pageButton(Page.Chart, "square.grid.4x3.fill", "Chart")
                    pageButton(Page.List, "list.bullet", "List")
                    pageButton(Page.Edit, "square.and.pencil", "Edit")
                    pageButton(Page.Camera, "camera", "Camera")
                    pageButton(Page.Settings, "gear", "Settings")
                }
            }
            
            switch page {
            case .ByEye:
                ByEyeView()
            case .Chart:
                ChartView()
            case .List:
                ListView()
            case .Edit:
                EditView()
            case .Camera:
                CameraView()
            case .Settings:
                SettingsView()
            }
            
            Spacer()
        }

It is not much longer than my original version. It is not as pretty is it could be - when you overflow the title bar you get half an icon, where an ellipsis would be better. But I can fix that later.

Upvotes: 1

Related Questions