UINavigationController interactivePopGestureRecognizer working abnormal in iOS7
This code can wroks well for me.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return [gestureRecognizer isKindOfClass:UIScreenEdgePanGestureRecognizer.class];
}
InteractivePopGestureRecognizer causing app freezing
I had similar problem with freezing interface when using swipe-to-pop gesture.
In my case the problem was in controller1.viewDidAppear I was disabling swipe gesture: self.navigationController.interactivePopGestureRecognizer.enabled = NO
. So when user started to swipe back from contorller2, controller1.viewDidAppear was triggered and gesture was disabled, right during it's work.
I solved this by setting self.navigationController.interactivePopGestureRecognizer.delegate = self
in controller1 and implementing gestureRecognizerShouldBegin:
, instead of disabling gesture recognizer:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)] &&
gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
return NO;
}
return YES;
}
In iOS7, abnormal popping action of UINavigationController with edge swiping
I found a solution to this problem.
What I did was in my UINagivationController class, set a BOOL property called
@property (nonatomic, assign) BOOL interactivePopGestureComplete;
and then in init of that UINagivationController
self.interactivePopGestureComplete = YES;
if([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
[self.interactivePopGestureRecognizer addTarget:self action:@selector(defaultGestureAction:)];
and the action method like this:
- (void)defaultGestureAction:(UIGestureRecognizer *)sender
{
if(sender.state == UIGestureRecognizerStateBegan || sender.state == UIGestureRecognizerStateChanged)
self.interactivePopGestureComplete = NO;
else
self.interactivePopGestureComplete = YES;
}
and in viewWillAppear: or viewWillLayoutSubviews: (in the viewControllers where there is this problem), wherever in these two methods you are updating the navigationBarButtons and the navigationBar, just check if
// Lets say our UINagivationController class name is NavigationCon
NavigationCon *navCon = (NavigationCon *)self.navigationController;
if(navCon.interactivePopGestureComplete)
{
// only then update navigationBar
}
How to enable back/left swipe gesture in UINavigationController after setting leftBarButtonItem?
First set delegate in viewDidLoad:
self.navigationController.interactivePopGestureRecognizer.delegate = self;
And then disable gesture when pushing:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
[super pushViewController:viewController animated:animated];
self.interactivePopGestureRecognizer.enabled = NO;
}
And enable in viewDidDisappear:
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
Also, add UINavigationControllerDelegate
to your view controller.
Canceling interactive UINavigationController pop gesture does not call UINavigationControllerDelegate methods
For anyone interested, I have found 2 ways to work around this at the UINavigationControllerDelegate
level.
Use KVO to observe the
state
property of theinteractivePopGestureRecognizer
. Unfortunately, canceling the transition does not change the state toUIGestureRecognizerStateFailed
but instead justUIGestureRecognizerStateEnded
, so you would need to write a bit of additional code to keep track of what happened if you needed to discern between a cancelled or completed pop.After testing it, this is probably the better solution: Use the
navigationController:willShowViewController:animated:
method to add a notification block to the transition coordinator. It looks something like this:- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
[[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
if([context isCancelled])
{
UIViewController *fromViewController = [context viewControllerForKey:UITransitionContextFromViewControllerKey];
[self navigationController:navigationController willShowViewController:fromViewController animated:animated];
if([self respondsToSelector:@selector(navigationController:didShowViewController:animated:)])
{
NSTimeInterval animationCompletion = [context transitionDuration] * (double)[context percentComplete];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((uint64_t)animationCompletion * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self navigationController:navigationController didShowViewController:fromViewController animated:animated];
});
}
}
}];
}
I was hesitant to use this solution at first because the documentation was unclear about whether or not you could set more than one of these (since in that case, if an unknowing view controller also set its own notification block, it could potentially either replace this one or get replaced by this one). After testing it though, it appears that it is not a 1:1 relationship and you can add multiple notification blocks safely.
edit
I edited the code above to delay the navigationController:didShowViewController:animated:
call to only be called when the animation is supposed to be completed to more closely match the expected default behavior.
How can I implement drag right to dismiss a View Controller that's in a navigation stack?
Made a demo project in Github
https://github.com/rishi420/SwipeRightToPopController
I've used UIViewControllerAnimatedTransitioning
protocol
From the doc:
// This is used for percent driven interactive transitions, as well as for container controllers ...
Added a UIPanGestureRecognizer
to the controller's view. This is the action of the gesture:
func handlePanGesture(panGesture: UIPanGestureRecognizer) {
let percent = max(panGesture.translationInView(view).x, 0) / view.frame.width
switch panGesture.state {
case .Began:
navigationController?.delegate = self
navigationController?.popViewControllerAnimated(true)
case .Changed:
percentDrivenInteractiveTransition.updateInteractiveTransition(percent)
case .Ended:
let velocity = panGesture.velocityInView(view).x
// Continue if drag more than 50% of screen width or velocity is higher than 1000
if percent > 0.5 || velocity > 1000 {
percentDrivenInteractiveTransition.finishInteractiveTransition()
} else {
percentDrivenInteractiveTransition.cancelInteractiveTransition()
}
case .Cancelled, .Failed:
percentDrivenInteractiveTransition.cancelInteractiveTransition()
default:
break
}
}
Steps:
- Calculate the percentage of drag on the view
.Begin:
Specify which segue to perform and assignUINavigationController
delegate. delegate will be needed forInteractiveTransitioning
.Changed:
UpdateInteractiveTransition with percentage.Ended:
Continue remaining transitioning if drag 50% or more or higher velocity else cancel.Cancelled, .Failed:
cancel transitioning
References:
- UIPercentDrivenInteractiveTransition
- https://github.com/visnup/swipe-left
- https://github.com/robertmryan/ScreenEdgeGestureNavigationController
- https://github.com/groomsy/custom-navigation-animation-transition-demo
Related Topics
Delete Cell from Uicollectionview Without Reloading from Top
How to List (Almost) All Emojis in Swift for iOS 8 Without Using Any Form of Lookup Tables
Implementing Uitextfielddelegate with Swift
How to Detect a Dual Core CPU on iOS
Endless Scrolling Background in Spritekit
iOS 8 Nsinternalinconsistencyexception
Setneedsdisplayinrect: Causes the Whole View to Be Updated
How to Properly Use Class Extensions in Swift
Autosizing Cells: Cell Width Equal to the Collectionview
Info.Plist File for React Native iOS App Using Expo Sdk
Coreanimation - Opacity Fade in and Out Animation Not Working
Peripheral and Central at the Same Time on iOS
Malloc Error "Can't Allocate Region" Failed with Error Code 12. Any Idea How to Resolve This
Does Nsthread Create Autoreleasepool Automatically Now
How to Make Uipageviewcontroller Reuse Controller Like Tableview Reuse Cell