Search code examples
swiftswiftuitabviewpagetabviewstyle

How to detect swiping (and execute an action upon it) with an iOS14 TabView's PageTabViewStyle() page-swipe?


Using Swift5.3.2, iOS14.4.2, XCode12.4,

I am struggling with this new iOS14 feature for creating PageViews in SwiftUI.

It all works, except I cannot detect the moments of swipe !

I tried using .onChange(selectionIndex) but as soon as I create this change-listener to the TabView's selectionIndex, the swipe stalls. It is no longer smooth.

struct MyView: View {

    @State var selectionIndex = 0

    var body: some View {

        TabView(selection: $selectionIndex) {
            ForEach(mediaList.paths.indices, id: \.self) { index in
               SomePageView(media: mediaList.paths[index])
            }
        }
        .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
        .onChange(of: selectionIndex) { newIdx in
            
            customDotsService.selectedIndex = newIdx  // because TabView's PageViewStyle does not have little dots that can be placed somewhere else on screen...
            sendCommand(index: newIdx) // some background task to send commands to other services...
        }
    }
}

Even if my onChange(selectionIndex) { newIdx in ... } remains completely empty, the swipe stalls and memory goes to the loops.

It is really an annoying problem and I am on it since 4 days non-stop.

Any help highly appreciated.

What I need from Apple: a working PageView that has correct life-cycle methods .onAppear(), .onChange(selectionIndex) and its little-Dots-Indicator that can be placed anywhere on screen. Help universe, help that we get this in SwiftUI.


Solution

  • I implemented this solution and it works as expected. Need to add tag value to View which is used in ForEach, then call onChange block and you'll able to detect swiping there.

    struct OnboardingPagerView: View {
        @ObservedObject private var viewModel = OnboardingPagerViewModel()
        @State private var selectedPageIndex = 0
    
        var body: some View {
            VStack {
                TabView(selection: $selectedPageIndex) {
                    ForEach(viewModel.items, id: \.self) { item in
                        TitleWithDescriptionView(title: item.title, description: item.description)
                            .tag(item.index)
                    }
                }
                .onChange(of: selectedPageIndex) { newValue in
                    debugPrint("[a]: new value \(newValue)")
                }
                .tabViewStyle(.page(indexDisplayMode: .always))
                .indexViewStyle(.page(backgroundDisplayMode: .never))
            }
        }
    }