0

I have a set of seven pages. I have made my own scrollable view.

I have a working solution where I have the current page and attach gestures to it like this...

switch page {
        case .EyeTest:
            EyeTestView(sd: $sd)
                .gesture(DragGesture(minimumDistance: swipeMin)
                    .onEnded { value in
                        if value.translation.width < -swipeMin { page = Page.Tone }
                    } )
        ...`

This worked. It was rather clunky, but I could animate the drags better if I worked at it. However, the tab view worked beautifully with TabView for up to five entries. So, I thought I might be able to span the solution using two tab views. Here is what I did, concluding the custom scrollbar at the top...

        VStack() {
            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.ByEye, anchor: .center) }
            .onChange(of: page) { page in
                withAnimation {
                    proxy.scrollTo(page, anchor: .center)
                }
            }
        }

        if (page == Page.EyeTest || page == Page.Tone || page == Page.Chart) {
            TabView(selection:$page) {
                EyeTestView(sd: $sd).tag(Page.EyeTest)
                ToneView(sd: $sd).tag(Page.Tone)
                ChartView(sd: $sd).tag(Page.Chart)
                ByEyeView(sd: $sd).tag(Page.ByEye)
                ListView(sd: $sd).tag(Page.List)
            }.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
        } else {
            TabView(selection:$page) {
                ChartView(sd: $sd).tag(Page.Chart)
                ByEyeView(sd: $sd).tag(Page.ByEye)
                ListView(sd: $sd).tag(Page.List)
                CameraView(sd: $sd).tag(Page.Camera)
                SettingsView(sd: $sd).tag(Page.Settings)
            }.tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
        }
    }`

However, this does not scroll past the 5th entry.

I would be interested to know why this doesn't work, but I would be happy to replace it with something equivalent that does work. I would also like to know how people handle things like books, which have lost more pages. I feel I ought to make two synchronised scrollable lists.

Yes, maybe it is bad API to have a flat menu with more than five entries. However, some of the ones at the end of the list are rarely used, but need to be discoverable.

I am using Xcode 14.0.1 with iOS 16.0.

Richard Kirk
  • 281
  • 1
  • 12

1 Answers1

0

Here is a solution that seems to work for me. It is quite close to my original bodge: we have two TabViews, and swap between them depending on the current page so we can always have the neighbours on either side.

I had written my own scrollable index to replace the TabView indexes, but you might make do with the original fittings. I have only been doing Swift for a month, so this may not be the best code, so please post your improvements.

`
enum Page { case EyeTest case Tone case Chart case ByEye case List case Camera case Settings }

@AppStorage("menu") private var menu = "Measure"
@State private var page = Page.ByEye

func setMenu(_ page: Page) {
    menu = (page == Page.EyeTest || page == Page.Tone || page == Page.Chart ) ? "Test" : "Measure"
}

func pageButton(_ select: Page, _ icon: String, _ title: String, _ proxy: ScrollViewProxy) -> some View {
    return Button {
        page = select
        setMenu(page)
        withAnimation {
            proxy.scrollTo(select, anchor: .center)
        }
    } label: {
        VStack {
            Image(systemName: icon)
            Text(title)
        }
        .frame(width: tabW)
    }
    .foregroundColor( page == select ? Color.white : Color.gray )
    .id(select)
}


var body: some View {
    
    VStack() {

        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)
                }
            }
        }

        if (menu == "Test") {
            TabView(selection:$page) {
                EyeTestView(sd: $sd).tag(Page.EyeTest)
                ToneView(sd: $sd).tag(Page.Tone)
                ChartView(sd: $sd).tag(Page.Chart)
                ByEyeView(sd: $sd).tag(Page.ByEye)
                SettingsView(sd: $sd).tag(Page.Settings)
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
            .onChange(of: page) { page in
                setMenu(page)
            }
            
        } else {
            TabView(selection:$page) {
                ChartView(sd: $sd).tag(Page.Chart)
                ByEyeView(sd: $sd).tag(Page.ByEye)
                ListView(sd: $sd).tag(Page.List)
                CameraView(sd: $sd).tag(Page.Camera)
                SettingsView(sd: $sd).tag(Page.Settings)
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode:.never))
            .onChange(of: page) { page in
                setMenu(page)
            }
        }

`

Richard Kirk
  • 281
  • 1
  • 12