swiftuitagstabviewswiftui-ontapgesture

TabView tag & Tap Gesture Wont Work Together


I am building something similar to an Instagram story. But whatever I tried either tabview tag or tap gesture works. I could not make them both work together. The purpose is if I tap the left or right side of the view it will jump to another story of the same storyModel. if I scroll tabview it will jump to another storyModel.

This is the extension I have tried

struct OnTap: ViewModifier {
    let response: (CGPoint) -> Void
    
    @State private var location: CGPoint = .zero
    func body(content: Content) -> some View {
        content
            .onTapGesture {
                response(location)
            }
            .simultaneousGesture(
                DragGesture(minimumDistance: 0)
                    .onEnded { location = $0.location }
            )
    }
}

extension View {
    func onTapGesture(_ handler: @escaping (CGPoint) -> Void) -> some View {
        self.modifier(OnTap(response: handler))
    }
}

struct OnTapAndTagAction: ViewModifier {
    let onTap: (CGPoint) -> Void
    let index: Int

    func body(content: Content) -> some View {
        content
            .overlay(
                Rectangle()
                    .fill(Color.clear)
                    .onTapGesture { tapLocation in
                        onTap(tapLocation)
                    }
            )
            .tag(index)
    }
}

extension View {
    func onTapGestureAndTag(index: Int, _ handler: @escaping (CGPoint) -> Void) -> some View {
        self.modifier(OnTapAndTagAction(onTap: handler, index: index))
    }
}

And this is how I use it

struct StoryDetailView: View {
    ZStack {
        Color.storyNotchPurple
            .edgesIgnoringSafeArea(.top)
        TabView(selection: $selectedStoryId) {
            Color.storyNotchPurple.edgesIgnoringSafeArea(.top)
            ForEach(storyModelArray.indices, id: \.self) { index in
            GeometryReader {...}
                .onTapGestureAndTag(index: index) { tapLocation in
                    if tapLocation.x < screenWidth / 2 {
                        if currentStoryIndex > 1 {
                            currentStoryIndex -= 1
                        }
                    } else {
                        if currentStoryIndex < progressCount {
                            currentStoryIndex += 1
                        }
                    }
                }
        }
    }
    .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
    .edgesIgnoringSafeArea(.top)
}

Solution

  • instead of tap gesture I have used buttons and z index

    HStack {
        Button(action: {
            if currentStoryIndex > 1 {
                currentStoryIndex -= 1
            }
        }) {
            Color.clear
                .contentShape(Rectangle())
                .frame(width: screenWidth / 2, height: g.size.height - 100)
        }
        .buttonStyle(PlainButtonStyle())
    
        Button(action: {
            if currentStoryIndex < progressCount {
                currentStoryIndex += 1
            }
        }) {
            Color.clear
                .contentShape(Rectangle())
                .frame(width: screenWidth / 2, height: g.size.height - 100)
        }
        .buttonStyle(PlainButtonStyle())
    }
    .zIndex(1)