UINavigationController Interactive Pop Gesture Not Working?
Asked Answered
A

11

39

So I have a navigation controller in my built for iOS 7 app. The titleView is visible, as well as the back button and navigation bar its self. For some reason, the interactive pop gesture (swipe from the left edge) isn't working. Nothing happens. When I log the gesture, it is not nil. Is there anything special I have to do to enable this functionality? What could cause it not to work?

Auscultate answered 22/9, 2013 at 16:52 Comment(0)
A
29

Eh, looks like I just had to set the gesture delegate and implement the following:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {

    return YES;

}
Auscultate answered 22/9, 2013 at 19:37 Comment(1)
Ive checked this but very creepy problem, when I have this fixed then if the user slides half way and then come back i see the next controller overlapped on the previous view controller. I see the Previous view controller and the next overlapped is transparent like transparent glass. So user happens to be locked this state. Any idea. This is something View did appear / will appear handling issue.Polka
D
81

I have found that when using custom back buttons, the interactive pop gesture stops working (my take is that Apple cannot foresee how your custom back button will behave, so they disable the gesture).

To fix this, as other mentioned before, you can set the interactivePopGestureRecognizer.delegate property to nil.

In Swift, this can easily be done across your entire application by adding an extension for UINavigationController like this:

extension UINavigationController {

    override public func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = nil
    }

}

Updated answer

Seems like setting the delegate to nil causes the app UI to freeze in some scenarios (eg. when the user swipes left or right on the top view controller of the navigation stack).

Because gestureRecognizerShouldBegin delegate method cannot be handled in an extension, subclassing UINavigationController seems like the best solution:

class NavigationController: UINavigationController, UIGestureRecognizerDelegate {

    /// Custom back buttons disable the interactive pop animation
    /// To enable it back we set the recognizer to `self`
    override func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

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

}
Debris answered 22/7, 2016 at 18:0 Comment(5)
This seems to work, but it turns out that under certain circumstances it freezes the app. I think it happens when the interactive pop gesture is triggered while a view controller is being pushed. A safer way would be to subclass UINavigationController. In the subclass set the interactivePopGestureRecognizer.delegate to self and prevent the interactive pop gesture from being recognized while a new view controller is pushed.Paraph
Indeed, I have found that issue too, seems to affect only the top view controller of the navigation controller stack. I have updated my answer with a different solution based in subclassing.Debris
@Eneko Alonso perfect! Just as you said, if user swipes left or right and then taps a button - push view controller is glitched. Subclassing fixes the problem 100%Zoogeography
@EnekoAlonso Im having a similar issue when I set the navigation bar hidden. I loose the swipe back functionality. Is there anyway you can help out with this? I posted this question for clarity: #48954913Hervey
navigationItem.leftItemsSupplementBackButton = true this should do the trick.Midget
A
29

Eh, looks like I just had to set the gesture delegate and implement the following:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {

    return YES;

}
Auscultate answered 22/9, 2013 at 19:37 Comment(1)
Ive checked this but very creepy problem, when I have this fixed then if the user slides half way and then come back i see the next controller overlapped on the previous view controller. I see the Previous view controller and the next overlapped is transparent like transparent glass. So user happens to be locked this state. Any idea. This is something View did appear / will appear handling issue.Polka
N
17

Look at this response and comments. All you have to do is set your navigation controller's interactive pop gesture recognizer's delegate to nil:

self.navigationController.interactivePopGestureRecognizer.delegate = nil;

Setting it to a casted self to id<UIGestureRecognizerDelegate> also works because all methods in the protocol are optional, but I think setting the delegate to nil is more appropriate in this case.

Naylor answered 20/7, 2015 at 15:0 Comment(2)
i only had success with this when in viewDidAppearBorodino
This "works" but shortly after causes very strange issues to the point of freezing the app (tested with iOS 8, device)Concussion
R
15

My answer is based on Eneko's answer but uses only an extension on UINavigationController and works in Swift 5:

extension UINavigationController: UIGestureRecognizerDelegate {

    override open func viewDidLoad() {
        super.viewDidLoad()
        interactivePopGestureRecognizer?.delegate = self
    }

    public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        return viewControllers.count > 1
    }
}
Roadbed answered 27/8, 2019 at 9:21 Comment(2)
it is not working to me with IOS 13. Are you have any suggestion?Snakemouth
Worked with ios 13.1Sweetbrier
A
13

If you feel you have tried all solutions and stretching your head then you're at the right place.

Goto simulator > Window > Enable Show Device Bezels

enter image description here

Now tried to simulate swipe to back gesture.

Ardennes answered 31/3, 2020 at 13:0 Comment(2)
This is so stupid, but it works! Why have Apple changed that?... Thx a bunch!Oculus
Thanks a lot SoftDesigner ✅.Toronto
R
12

You can put this line in the viewDidLoad method.

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
Rasure answered 17/9, 2014 at 4:25 Comment(0)
P
8

Maybe someone may find this helpful.

If you want to hide the navigation bar but use normal swipe gestures to go back and other navigation controller features, you should use: (navigationBar)

self.navigationController?.navigationBar.isHidden = true

If you want to disable navigation bar (hide navigation bar, disable swipe for back) but want to push viewcontroller you should use: (isNavigationBarHidden)

self.navigationController?.isNavigationBarHidden = true

Update 7-DEC-2018:

Recommended way:

In case that your first controller use hidden navigation bar, but next childs use navigation bar, when you come back to base view controller you will see a black bar in transition in place of navigation bar. This will be fixed very easy if you use in first viewcontroller(parent):

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    self.navigationController?.setNavigationBarHidden(true, animated: animated)
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
Papeete answered 22/6, 2018 at 13:0 Comment(0)
P
6

The more worked out answer was both Aaron and lojals

First Customise the Navigation controller and then put this code in the class

In ViewDidload put this line:

self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;

And in class write this function

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES;}
Polka answered 4/12, 2015 at 2:31 Comment(0)
A
3

In Swift 4, I have a UITableView inside my view controller, I solved this issue with:

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.interactivePopGestureRecognizer?.delegate=nil
}
Acupuncture answered 27/4, 2018 at 6:5 Comment(0)
B
0

Generically add interactive pop gesture to the whole app.

XCODE: 9.0, Swift: 4.0

Preferably create UINavigationController in AppDelegate.swift

  1. Create a navigation controller
// I created a global variable, however not necessarily you will be doing this way
var nvc: UINavigationController!
  1. implement UIGestureRecognizerDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, UIGestureRecognizerDelegate {
  1. Instantiat UINavigationController in application didFinishLaunchingWithOptions function
nvc=UINavigationController()

// For interactive pop gesture
nvc.navigationBar.isHidden=true
nvc?.interactivePopGestureRecognizer?.delegate=self
  1. Extra step, add controller to navigation controller in application didFinishLaunchingWithOptions function
window=UIWindow()
window?.rootViewController=nvc
window?.makeKeyAndVisible()

// BaseViewController is sample controller i created with xib
nvc.pushViewController(BaseViewController(), animated: true)
  1. Implement gusture recognizer, add below code to AppDelegate.swift
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }



Note: See other post in this section for the difference between

self.navigationController?.navigationBar.isHidden=true

And

self.navigationController?.isNavigationBarHidden = true
Bunyan answered 26/2, 2019 at 12:52 Comment(0)
M
0

If you use custom back buttons, you have to do this:

navigationItem.leftItemsSupplementBackButton = true

That way the pop gesture recognizer is still active.

If you need more customization, you can use the solution provided by Eneko Alonso

Midget answered 15/6 at 3:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.