37

if I set a custom Back Button (which everyone wants, hiding the ugly text ;-) ) and using .navigationBarBackButtonHidden, the standard Swipe Back gesture on the navigation controller does not work. Is there a way to get this back and having a custom back button?

For Example:

NavigationView {
    NavigationLink(destination: DummyViewer())
     {
       Text("Go to next view"
    } 
 }
struct DummyViewer: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var body: some View {
        Text("Hello, World!").navigationBarBackButtonHidden(true)
            .navigationBarItems(leading:
                Button(action: { self.presentationMode.wrappedValue.dismiss()}) {
                    Text("Custom go back")
                }
        )
    }
}

If I do so, I cannot swipe back to the previous view, seems the gesture is then disabled... How to get it back?

maxmitz
  • 258
  • 1
  • 4
  • 17
sTOOs
  • 564
  • 1
  • 5
  • 9
  • 1
    Does this answer your question? [Hide navigation bar without losing swipe back gesture in SwiftUI](https://stackoverflow.com/questions/59921239/hide-navigation-bar-without-losing-swipe-back-gesture-in-swiftui) – iSpain17 Feb 17 '20 at 19:52

7 Answers7

98

Nothing I found about creating a custom NavigationView worked but I found that by extending UINavigationController I was able to have a custom back button and the swipe back gesture.

extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}
Nick Bellucci
  • 2,296
  • 1
  • 11
  • 8
25

I would like to integrate the answer given by Nick Bellucci to make the code also works in other circumstances, e.g. when the child view of the NavigationView is a ScrollView, or a View that is listening for Drag gestures.

extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    } 

    // To make it works also with ScrollView
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        true
    }
}
2

I've just created a hack which will not animate view but it works


extension View {
    func onBackSwipe(perform action: @escaping () -> Void) -> some View {
        gesture(
            DragGesture()
                .onEnded({ value in
                    if value.startLocation.x < 50 && value.translation.width > 80 {
                        action()
                    }
                })
        )
    }
}

Usage

struct TestView: View {
    @Environment (\.presentationMode) var mode
    var body: some View {
        VStack {
            Color.red
        }
        .onBackSwipe {
            mode.wrappedValue.dismiss()
        }
    }

}
1

I would like to integrate the answer given by Niccolò Fontana who integrate the answer given by Nick Bellucci to make the code even better.

Niccolò Fontana makes it also works when the child view of the NavigationView is a ScrollView, or a View that is listening for Drag gestures.

But in that case you will simultaneously scroll and swipe. That fills annoying. And if you want original behavior, you should use that snippet:

extension UINavigationController: UIGestureRecognizerDelegate {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    } 

    // To make it works also with ScrollView
    // public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    //     true
    // }

    // To make it works also with ScrollView but not simultaneously 
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        true
    }

}
Dmitry
  • 242
  • 1
  • 9
  • 20
0

You might just wanna add this in your code and you can easily use the swipe back gesture with a custom back button or navigation bar hidden.

extension UINavigationController {
    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = nil
    }
}
Vraj Shah
  • 31
  • 5
-1

You can set the title to an empty string. So back bar button title will be empty:

struct ContentView: View {

    var body: some View {

        NavigationView {

            NavigationLink(destination: Text("Here you are")) {

                Text("Next").navigationBarTitle("")
            }
        }
    }
}

You can set the title onAppear or onDisappear if you need to.

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • Hi, I added some more information. It is about the Back Button and the Swipe gesture which it causes - or not. – sTOOs Dec 08 '19 at 13:51
-1

If it's still actual, here I answered, how to set custom back button and save swipe back gesture.

Hrabovskyi Oleksandr
  • 3,070
  • 2
  • 17
  • 36