8

I'm trying to keep track of what page the user is on in a TabView that is PageTabViewStyle in SwiftUI but I can't figure out the best way to keep track of the page index? Using .onAppear doesn't work well as it gets called multiple times and pages 2 and 3 get called even when not on the screen.

@State var pageIndex = 0

    var body: some View {
    VStack {
        Text("current page = \(0) ")
        TabView {
            Text("First")
                .onAppear {
                    pageIndex = 0
                }
            Text("Second")
            .onAppear {
                    pageIndex = 1
                }
            Text("Third")
            .onAppear {
                    pageIndex = 2
                }
        }
         .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
         }
    }
}
GarySabo
  • 5,806
  • 5
  • 49
  • 124

2 Answers2

12

You can pass a selection binding to the TabView and use tag to identify the pages:

struct ContentView: View {
    @State var pageIndex = 0
    
    var body: some View {
        VStack {
            Text("current page = \(pageIndex) ")
            TabView(selection: $pageIndex) {
                Text("First").tag(0)
                Text("Second").tag(1)
                Text("Third").tag(2)
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
        }
    }
}

Note that in your original code, it was always going to say current page = 0 because you weren't interpolating the pageIndex variable into the string

jnpdx
  • 45,847
  • 6
  • 64
  • 94
  • I would recommend using enums just to make it look cleaner :) – Rikh Mar 07 '21 at 22:42
  • @Rikh: you mean using enum in tag and instead of Integer, right? – ios coder Mar 07 '21 at 22:46
  • Yep! Enum to keep track of current tab as well. Kinda like `@State private var current : Tabs` – Rikh Mar 07 '21 at 22:51
  • Yes, that is certainly an option. Not sure how much 'cleaner' it looks since unfortunately the `tag()` doesn't do type inference based on the selection type. Maybe in a future version of SwiftUI. I went with Int because that's what the OP's question was set up as. – jnpdx Mar 07 '21 at 22:55
  • Wow this is great. I thought the tag was only a reference for the developer and I'd have to get and translate it into the `$pageIndex` somehow, but it already does that! – Big_Chair Dec 23 '21 at 14:05
  • Make sure to **define the State value initially and don't leave it nil**, otherwise, it won't work. – Tamás Sengel Dec 22 '22 at 23:22
2

If you want to be able to get the current page/index and act on that data, what I found useful is using the .onChange() modifier on the view, like so.

TabView(selection: $currentIndex) {
     Text("First view")
         .tag(0)
                
     Text("Middle view")
         .tag(1)
                
     Text("Last view")
         .tag(2)
}
.tabViewStyle(.page(indexDisplayMode: .never))
.onChange(of: currentIndex) { newValue in
     print("New page: \(newValue)")
}

Hope that helps!

Tim Isenman
  • 147
  • 10