SwiftUI: stuck on infinite page view implementation

Viewed 769

I'm trying to create a PageView in pure SwiftUI. There's my test code below. And everything works as expected but the DragGesture. It just doesn't call 'onEnded' function. Never. How can I fix it?

struct PageView<V: View>: Identifiable {
    let id = UUID()
    var content: V
}

struct InfinitePageView: View {
    @State private var pages: [PageView] = [
        PageView(content: Text("Page")),
        PageView(content: Text("Page")),
        PageView(content: Text("Page"))
    ]
    
    @State private var selectedIndex: Int = 1
    @State private var isDragging: Bool = false
    
    private var drag: some Gesture {
        DragGesture()
            .onChanged { _ in
                self.isDragging = true
            }
            .onEnded { _ in
                self.isDragging = false
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                    resolvePages()
                }
            }
    }
    
    var body: some View {
        NavigationView {
            TabView(selection: $selectedIndex) {
                ForEach(pages) { page in
                    page.content
                        .tag(pages.firstIndex(where: { $0.id == page.id })!)
                }
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
            .gesture(drag)
            .onChange(of: selectedIndex, perform: { value in
                guard !isDragging else { return }
                DispatchQueue.main.async {
                    resolvePages()
                }
            })
        }
    }
    
    private func resolvePages() {
        if selectedIndex > 1 {
            addNextPage()
        }
        if selectedIndex < 1 {
            addPreviousPage()
        }
    }
    
    private func addNextPage() {
        pages.append(PageView(content: Text("Page")))
        pages.removeFirst()
        selectedIndex = 1
    }
    
    private func addPreviousPage() {
        pages.insert(PageView(content: Text("Page")), at: 0)
        pages.removeLast()
        selectedIndex = 1
    }
}
1 Answers

This is a known issue with SwiftUI

the DragGesture that you setup likely gets overridden by the DragGesture within the TabView.

Can detect onEnded with the setup in this post Detect DragGesture cancelation in SwiftUI but that deactivates the TabView/Page gestures.

Your onChange code for selectedIndex runs once the new page is selected and would act should act the same as onEnded.

Related