UIControllerView Back swipe stops working when setting navigationController.interactivePopGestureRecognizer.delegate = self; - ios

As title says. I had one controller where I was setting interactivePopGestureRecognizer.delegate to handle logic when to allow back swipe gesture and when not. It worked. But now I noticed that once I setup the delegate, the back swipe stops working. It really causes that one line of code. But why?
Yes, the controller where I used to handle the backswipe logic had everything needed (UIGestureRecognizerDelegate protocol, gestureRecognizerShouldBegin delegate method with return YES), but as I say, I discovered in another controller that by just calling the one line of the following code, back swipe doesn't work anymore. (Yes this another controller conforms to UIGestureRecognizerDelegate protocol)
self.navigationController.interactivePopGestureRecognizer.delegate = self;
It doesn't help if I add also:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
return YES;
}
or
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
I wonder what is causing this? If I don't call that one line of code, back swipe works! And it even worked in the another controller where I handled the logic as I said.
Edit: I was setting the delegate from viewDidLoad. I tried also from viewDidAppear, but nothing.

The issue is because you are setting multiple ViewController as the delegate. Once you set any ViewController as the UIGestureRecognizerDelegate delegate, that ViewController is responsible for handling the gesture and previously set any delegate will be invalid
To fix the issue you can set the delegate again when view appears in viewWillAppear
self.navigationController.interactivePopGestureRecognizer.delegate = self;

For some reason, if I add the following code to controller, back swipe again works. Maybe it is because i have scrollview in the controller view, but it was working before even without the following code and then it stopped. Strange. (May be i didnt have tableview on the controller where it worked, i dont remember, but i was trying it even with hidden table when it stopped working)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
As said here.

Related

UINavigation Swipe Back Gesture use with UIViewController?

In my application I am using the standard UIViewController structure around my app. However I have views which I transition to using a UIStoryboardSegue push. In that UIViewController I am doing this in the viewDidLoad:
self.navigationController.interactivePopGestureRecognizer.delegate = self;
Then I have this delegate method as well to enable the swipe back gesture:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
The problem is, in my original view I presented from, I present a custom menu programmatically in the viewWillAppear using a typical approach like this:
[self.view addSubview:menu];
This works fine if I were to go back to my main menu using
[[self navigationController] popViewControllerAnimated:YES];
However, if I use the swipe back gesture, the view never presents even though the viewWillAppear clearly is called. Is my UINavigationController the issue for this or is something else going on here that I simply do not see?

viewWillAppear called but viewDidAppear not called after pushViewController

In my app, sometimes pushViewController fails for no reason and what happens is very weird. The navigationBar and navigationItem change but the ViewController is not pushed. Then I can tap nothing on the screen. I find that viewWillAppear is called but viewDidAppear isn't called. I push the home button of iPhone to enter background. After entering foreground again, the ViewController is pushed and viewDidAppear is called. I don't know why and when it happens.
normal viewDidAppear callstack
viewDidAppear after enterBackground callstack
If you can repro by:
Try using the left edge pop gesture when there are no view
controllers below it (i.e on root view controllers, your VC-Home
controller)
Try clicking on any UI elements after this.
Then Disable interactivePopGestureRecognizer when current viewController is the firstVC in navigation controller.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
reference:
iOS App Freezes on PushViewController
Is the viewcontroller which you pushed into the view hierarchy overwrite the viewWillAppear/viewDidAppear accidently without calling [super viewWillAppear/viewDidAppear:animated]?
You're probably accidentally calling [super viewDidLoad] inside your viewWillAppear method
For anyone having the same issue as me: Check all your custom views to see if you're not having an infinite loop of layoutSubviews. This is on of the things that happens in between a viewWillAppear and a viewDidAppear.
In my implementation I had a custom Tab bar controller and fore some reason viewDid Appear was empty, putting super calling solved it.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) //was missing this line
}
You know,if you overwrite navigationController.interactivePopGestureRecognizer.delegate and not restore it appropriately, this bug will appear,too

Is it necessary to call super in viewWillAppear?

I am trying to understand the scenario of the method calls to view did/will appear and disappear.
What I did is selecting the table cell (higlights in grey) , go to detail view and go back and deselect the selected row (remove the selected cell grey color).
Here are my methods:
-(void)viewDidAppear:(BOOL)animated {
DLog(#"%# did appear", self);
[super viewDidAppear:animated];
if (_isPushed) {
[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
_isPushed=NO;
}
}
-(void)viewWillAppear:(BOOL)animated {
DLog(#"%# will appear", self);
[super viewWillAppear:animated]; //If I remove this super call , then it works fine and there is no delay in deselecting the table cell
}
-(void)viewWillDisappear:(BOOL)animated {
DLog(#"%# will disappear", self);
[super viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
_isPushed=YES;
}
So , when I put breakpoint the flow goes like this:
without super call:
while pushing to new VC:
current viewWillDisappear //makes sense
new viewWillAppear //makes sense
current viewDidAppear // doesnt make sense , y this should get called as the view is already appeared?
current viewWillDisappear // make sense
current viewDidDisappear //make sense
new viewDidAppear //make sense
while coming back from pushed VC:
current viewWillDisappear
new viewDidDisappear
current viewDidDisappear
new viewDidAppear
with super call:
while pushing to new VC:
current viewWillDisappear
new viewWillAppear
current viewDidAppear
current viewWillDisappear
current viewDidDisappear
new viewDidAppear
while going back from pushed VC:
current viewWillDisappear
new viewDidDisappear
current viewDidDisappear
new viewDidAppear
The flow is pretty much the same either I use super call or not.
But the problem I am facing is, when I use super call in viewWillAppear, there is a delay(around >1second) in deselcting the cell.
If I dont use the super call in viewWillAppear , there is no delay and the cell is deselcting (around <0.5 seconds)
I am not sure to use super call or not.
Can anyone please tell me why there is a delay in deselecting the cell?
Yes, the documentation states you must:
Discussion
This method is called before the receiver's view is about
to be added to a view hierarchy and before any animations are
configured for showing the view. You can override this method to
perform custom tasks associated with displaying the view. For example,
you might use this method to change the orientation or style of the
status bar to coordinate with the orientation or style of the view
being presented. If you override this method, you must call super at
some point in your implementation.
Generally yes, call super. I've seen weird things happen in nav controllers when I forget.
In this case, if you have a UITableViewController, try using its clearsSelectionOnViewWillAppear flag to clear the selection for you.
Yes It's necessary to write super.

GLKViewControl, Storyboard and tap events

I have a problem with a GLKViewController (subclass) used in a Storyboard: it refuses to handle tap events.
I added a Tap Gesture Recognizer to the GLKViewController (subclass) in the Storyboard and linked it to a 'tapDetected' method:
- (IBAction) tapDetected:(id)sender {
NSLog(#"tap\n");
[self.navigationController setNavigationBarHidden: ![self.navigationController isNavigationBarHidden]];
} // tapDetected
This exactly how I manage - successfully - the taps in other view controllers in the same Storyboard. Only the GLKViewController does not receive the event: neither the log nor the navigation bar are affected by the tap.
I searched for any specific need of GLKViewControllers but could not find any. Do you have any idea and/or suggestion?
It looks like if I add a touchesBegan method everything works ok. So the question is: what's the point of the gesture recognizer in the Storyboard?

How to prevent user from leaving view controller?

I have an iPad app (XCode5, ARC, iOS7, Storyboards with a UITabBarController controlling the navigation). On one view, I have some required fields that I check for in -viewWillDisappear; if one of them is missing, I display an alert. The problem is I need to stay on that view until it's corrected. Unfortunately, the only place I can check for the required fields is in -viewWillDisappear.
Is there some way I can cause the view to complete the disappear and then go back to that same view? I have looked at SO and it doesn't appear to be a way, but I thought I'd ask anyway, just in case someone has figured out how to do it.. :D
You need to do
self.tabBarController.delegate = self
in your viewdidload and then implement the delegate method
- (BOOL)tabBarController:(UITabBarController *)tabBarController
shouldSelectViewController:(UIViewController *)viewController
{
if(conditions_satisfied)
return YES;
else
{
//show alert view here
return NO;
}
}
EDIT:It would appear that rdelmar was faster than me :)
You can set the delegate for the tab bar controller, and return NO from tabBarController:shouldSelectViewController: until whatever conditions you set are met.

Resources