I love the swipe pack thats inherited from embedding your views in a UINavigationController
. Unfortunately i cannot seem to find a way to hide the Naviga
Looks like solution provided by @ChrisVasseli is the best. I'd like to provide same solution in Objective-C because question is about Objective-C (see tags)
@interface InteractivePopGestureDelegate : NSObject <UIGestureRecognizerDelegate>
@property (nonatomic, weak) UINavigationController *navigationController;
@property (nonatomic, weak) id<UIGestureRecognizerDelegate> originalDelegate;
@end
@implementation InteractivePopGestureDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (self.navigationController.navigationBarHidden && self.navigationController.viewControllers.count > 1) {
return YES;
} else {
return [self.originalDelegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if (aSelector == @selector(gestureRecognizer:shouldReceiveTouch:)) {
return YES;
} else {
return [self.originalDelegate respondsToSelector:aSelector];
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.originalDelegate;
}
@end
@interface NavigationController ()
@property (nonatomic) InteractivePopGestureDelegate *interactivePopGestureDelegate;
@end
@implementation NavigationController
- (void)viewDidLoad
{
[super viewDidLoad];
self.interactivePopGestureDelegate = [InteractivePopGestureDelegate new];
self.interactivePopGestureDelegate.navigationController = self;
self.interactivePopGestureDelegate.originalDelegate = self.interactivePopGestureRecognizer.delegate;
self.interactivePopGestureRecognizer.delegate = self.interactivePopGestureDelegate;
}
@end
In my view controller without navigationbar I use
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
CATransaction.begin()
UIView.animate(withDuration: 0.25, animations: { [weak self] in
self?.navigationController?.navigationBar.alpha = 0.01
})
CATransaction.commit()
}
open override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
CATransaction.begin()
UIView.animate(withDuration: 0.25, animations: { [weak self] in
self?.navigationController?.navigationBar.alpha = 1.0
})
CATransaction.commit()
}
During the interactive dismissal the back button will shine through though, which is why I hid it.
My solution is to directly extend the UINavigationController
class :
import UIKit
extension UINavigationController: UIGestureRecognizerDelegate {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return self.viewControllers.count > 1
}
}
This way, all navigation controllers will be dismissable by sliding.
A hack that is working is to set the interactivePopGestureRecognizer
's delegate of the UINavigationController
to nil
like this:
[self.navigationController.interactivePopGestureRecognizer setDelegate:nil];
But in some situations it could create strange effects.
I've tried this and it's working perfectly : How to hide Navigation Bar without losing slide-back ability
The idea is to implement "UIGestureRecognizerDelegate" in your .h and add this to your .m file.
- (void)viewWillAppear:(BOOL)animated {
// hide nav bar
[[self navigationController] setNavigationBarHidden:YES animated:YES];
// enable slide-back
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
While most answers here are good, they seemingly have unintended side-effects (app breaking) or are verbose.
The most simple yet functional solution I could come up with was the following:
In the ViewController that you are hiding the navigationBar,
class MyNoNavBarViewController: UIViewController {
// needed for reference when leaving this view controller
var initialInteractivePopGestureRecognizerDelegate: UIGestureRecognizerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// we will need a reference to the initial delegate so that when we push or pop..
// ..this view controller we can appropriately assign back the original delegate
initialInteractivePopGestureRecognizerDelegate = self.navigationController?.interactivePopGestureRecognizer?.delegate
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
// we must set the delegate to nil whether we are popping or pushing to..
// ..this view controller, thus we set it in viewWillAppear()
self.navigationController?.interactivePopGestureRecognizer?.delegate = nil
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
// and every time we leave this view controller we must set the delegate back..
// ..to what it was originally
self.navigationController?.interactivePopGestureRecognizer?.delegate = initialInteractivePopGestureRecognizerDelegate
}
}
Other answers have suggested merely setting the delegate to nil. Swiping backwards to the initial view controller on the navigation stack results in all gestures to be disabled. Some sort of oversight, perhaps, of the UIKit/UIGesture devs.
As well, some answers here that I have implemented resulted in non-standard apple navigation behaviour (specifically, allowing for the ability to scroll up or down while also swiping backwards). These answers also seem a bit verbose and in some cases incomplete.