Liviu
Liviu

Reputation: 152

Swipe back when using a custom navigation bar and a TabView SwiftUI

I have a SwiftUI app which uses a custom navigation bar. Because of that, I need to handle the back navigation separately (both the back button and the swipe gesture). Everything went fine up until now, when I need to use a TabView to swipe between pages. The code below illustrates what I'm trying to achieve:

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            NavigationLink(destination: SecondView()) {
                Text("Go to second view")
            }
        }
    }
}

SecondView.swift

import SwiftUI

// Being able to go back by swiping
// https://stackoverflow.com/questions/59921239/hide-navigation-bar-without-losing-swipe-back-gesture-in-swiftui
extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }
    
    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}

struct SecondView: View {
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack(alignment: .leading) {
            Image(systemName: "chevron.left")
                .foregroundColor(Color(.systemBlue))
                .font(.title2)
                .onTapGesture {
                    self.presentationMode.wrappedValue.dismiss()
                }
                .padding()
            TabView {
                Text("Test 1")
                    .tag(1)
                Text("Test 2")
                    .tag(1)
                Text("Test 3")
                    .tag(3)
            }
            .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
        }
        .background(Color(.systemGray6))
        .navigationBarHidden(true)
    }
}

Note that I didn't add the custom navigation bar for the second view, I've just hidden the default navigation bar, as the custom bar is not needed to solve this problem.

When the user is inside the SecondView and presses the back button, everything works as expected. The problem appears when he tries to swipe back, as the swipe back gesture is captured by the TabView. I want to keep the 'swipe-between-pages' functionality of the TabView while being able to go back to ContentView when the user swipes right from the leftmost part of the screen.

This problem only appears when using TabViews, other types of content handle the swipe back gesture without problems.

To solve this problem, I could add a horizontal padding to the TabView like this:

TabView {
    // content
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.padding(.horizontal)

and then the user would have some space to swipe back, but this solution is not so good when some views inside the TabView need to take the whole width of the screen.

Is there any way to handle the swipe back gesture in this particular case? Maybe another possible solution would be customizing the TabView to ignore the drag gesture when the first view is presented and a swipe right gesture is captured (I don't know how to implement that).

Upvotes: 1

Views: 1358

Answers (2)

Hollycene
Hollycene

Reputation: 590

When you use the extension for 'being able to go back by swiping' provided in this answer (https://stackoverflow.com/a/76976498/19954370), the gestures used in a TabView should no longer collide with the gesture when swiping back to the root view.

However, for this extension to work, you must avoid using .navigationBarBackButtonHidden(true) and .navigationBarHidden(true) in your custom NavigationView view.

Upvotes: 0

Matt
Matt

Reputation: 11

I ran into this same problem. I solved it by wrapping my first tabview in with a geometryReader. When the bounds of that view is more than a quarter off the screen, I dismiss the view.

Upvotes: 1

Related Questions