popViewController not working in iOS 9 - ios

I have inherited my ViewController from BaseViewController and I have pushed my SecondViewController using storyboard ID.but, when I am trying to popview from second view to view controller its not working.
Here is my code:
dispatch_async(dispatch_get_main_queue(), ^{
[self showMesssgeonAlert:#"Success"];
[self hideProgress];
[self.navigationController popViewControllerAnimated:TRUE];
});
Even i tried with :
NSArray *controllerArray = self.navigationController.viewControllers;
for (id controller in controllerArray)
{
if ([controller isKindOfClass:[ViewController class]])
{
[self.navigationController popViewControllerAnimated:TRUE];
}
}
but,its not working in both the conditions.What should I do?Please help me.

Make sure you are pushing the controller SecondViewController on ViewController, If you are using segue then check the show type like (Push, Present Modally). If this is correct then check the navigation controller object, it should have an object.
Finally check the memory address of navigation controller in both the view controllers did load method. It should same.

You should double check:
Both the view controllers should be on same navigation controller stack.
I guess, second view controller is on different navigation controller.
How you're pushing to second view controller?

Try using appDelegates navigation controller so, try as follow:
dispatch_async(dispatch_get_main_queue(), ^{
[self showMesssgeonAlert:#"Success"];
[self hideProgress];
[((AppDelegate*)[[UIApplication sharedApplication]delegate]).navigationController popViewControllerAnimated:TRUE];
});

Split view controller may cause this issue, in my case, I removed it, and popViewController started working.

Related

How presentViewController works when called on viewController or on NavigationController?

What are the pros and cons of below two approaches of presenting View Controller ? What's the difference ?
UIViewController *abcVC = [self.storyboard instantiateViewControllerWithIdentifier:#"abcVC"];
[self presentViewController:abcVC];
or
UIViewController *abcVC = [self.storyboard instantiateViewControllerWithIdentifier:#"abcVC"];
[self.navigationController presentViewController:abcVC];
The first method, you mention will work only when the parent view controller is not having the navigation controller,
The second method you mention will work only when the parent view controller is the navigation controller,
but both method will present the view controller,
Correction here [self presentViewController:abcVC]; is deprecated method and new one is
[self presentViewController:abcVC animated:YES completion:nil];.
Both above mentioned will do same thing and you don't need to use self.navigationController to present a controller modally.
UINavigationController manage a stack of controllers and thats why we use it for push and pop purpose.Use UINavigationController if you need to push a controller and you want a native back button to switch back to previous controller.
i hope this will help you.

How to switch between view controllers and get rid of the previous one

In android, switching between activities, is fairly straightforward
you call
Intent intent = new Intent(this,NextActivity.class); <- define the next activity
startActivity(intent); <- start the next activity
finish(); < -get rid of the current activity
now in iOS i know how to do this:
UIViewController *nextviewcontroller = [[UIViewController alloc]initWithNibName:#"nextvc" bundle:nil];
[self presentViewcontroller:nextviewcontroller animated:YES completion:nil];
How do I get rid of the current view controller? so that currentviewcontroller dies after presenting nextviewcontroller ?
[self dismissViewController:YES]; doesnt seem to do the trick
the lifecycle methods viewWillDisappear and viewDidDisappear are called even if I don't call [self dismissViewController:YES];
i want "currentviewcontroller" to be removed from the memory, and from the viewcontroller stack, so that clicking "back" in "nextviewcontroller" will go to some thirdviewcontroller that was before currentviewcontroller
In iOS is different, since there's no concept of Activity and everything is more focused on the app itself (in Android you can mix activities from different apps). Therefore, there's no concept of "view controller stack".
The most similar concept is the "navigation stack" of navigation controllers, where you actually push and pop new view controller into some kind of linear navigation. A navigation bar is automatically created and populated with back buttons.
presentViewController will show your view controller modally upon the current one, but you can't thrash the presenting one since it's holding and containing ("defining context") the new one.
If you use a navigation controller for your navigation hierarchy (I don't know if you can), you can override the back button and use something like
UIViewController * prev = self.navigationController.viewControllers[self.navigationController.viewControllers.count -2 ]
[self.navigationController popToViewController:prev animated:YES]
With a modal view controller, you may try something like (I haven't tried but it may work)
[self.presentingViewController.navigationController popViewControllerAnimated:YES]
You should write one of these code into the target action of your close button.
iOS doesn't maintain a global stack of controllers in the way that Android does. Each app shows a controller at its root, and that one is responsible for showing the other controllers in the app. Controllers can display other controllers modally using presentViewcontroller:animated:completion: but the presenting controller remains underneath the presented one.
If your current controller is the root controller, then instead of using presentViewcontroller:animated:completion: you'd just do this:
self.view.window.rootViewController = nextViewController;
It's very common for the root controller to be a UINavigationController, which does manage a stack of controllers. If that is the case, and if your current controller is at the top of the stack, you'd do this:
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController pushViewController:nextViewController animated:YES];
If your setup is different, you'd do something different; it's hard to say what without knowing more. But it's most likely that you'd be in the UINavigationController case.
In the viewDidAppear of your nextviewcontroller you could add :
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSArray *controllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:controllers];
[newViewControllers removeObjectAtIndex:[controllers count]-2];
self.navigationController.viewControllers = newViewControllers;
}
There is nothing available like this in iOS but you can achieve it doing something like below
NSArray *viewControllers=[self.navigationController viewControllers];
NSMutableArray *newControllers=[[NSMutableArray alloc] init];
for(int i=[viewControllers indexOfObject:self];i<viewControllers.count;i++){
[newControllers addObject:[viewControllers objectAtIndex:i]];
}
[self.navigationController setViewControllers:[[NSArray alloc] initWithArray:newControllers]];
I have tried the method of storing all the view controllers in an array but it didn't work for me . When you try popViewController it will move to the View Controller which is last in the stack.
You can make 2 navigation controllers and switch between them and also switch between the view controllers of a particular Navigation Controller.
For eg.
You can switch between 2 Navigation Controller using the following code:
FirstNavController *fisrtView=[storyboard instantiateViewControllerWithIdentifier:#"firstnavcontroller"];
self.window.rootViewController = firstView;
}else{
SecondNavController *secondView=[storyboard instantiateViewControllerWithIdentifier:#"loginnavcontroller"];
self.window.rootViewController = secondView;
}
If your FirstNavController has 2 ViewControllers then you can switch between them using pushViewController
SecondViewController *sc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
[[self navigationController] pushViewController:sc animated:YES];
and popViewController
[self.navigationController popViewControllerAnimated:YES];

Generic way of Popping & Dismissing View Controllers

I am building an application in which, i have 3 ViewControllers.
Also i have created custom navigation controller.
A-ViewController -> This contains 2 buttons, 1st button is for opening B-ViewController & 2nd button is for C-ViewController.
From 1st button, I am using pushviewcontroller for B-ViewController, means B-ViewController is pushed from A-ViewController.
From 2nd button C-ViewController is presented using presentviewcontroller.
Now on pressing back button in both B & C ViewControllers, I have to use pop view controller in B-ViewController & dismiss in C-ViewController.
As currently i know the pages, but there should be a generic solution.
Is there any way to identify whether current navigation controller is pushed or popped.
As there are some pages which can be pushed or presented, but i dont want to set any bool variable. I need use the support from apple.
NSArray *arrViewControllers = [[AppDelegate sharedInstance].navigationController viewControllers];
NSLog(#"[arrViewControllers count] = %d",[arrViewControllers count]);
I am using the above code for fetching the list of view controllers in the stack.
But i am not able to identify whether it is pushed or presented.
Can anybody help me in this ?
You can check if your view controller exists in the navigation view controller's stack. Simply check it as
if([self.navigationController topViewController] == self){
//VC is the top most view controller
[self.navigationController popViewControllerAnimated:YES];
}else{
//You can put some checks here to be dead sure its a modally presented view controller
[self dismissViewControllerAnimated:YES completion:nil];
}
This is the code for Finding the navigation controller is presented or pushed from previous controller.
NSArray *arrViewControllers = [[AppDelegate sharedInstance].navigationController viewControllers];
UIViewController *viewController = [arrViewControllers lastObject];
if (viewController.presentedViewController)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else{
[self.navigationController popViewControllerAnimated:YES];
}

removeFromParentViewController removes all the view controller from the stack

In my app, I want to remove a view controller from the stack of view controllers and then navigate to a particular view controller. Say I have A,B,C,D,E view controller stack. I wish to remove E and then go to C. I use the following code
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allViewControllers)
{
if ([aViewController isKindOfClass:[noNetworkViewController class]])
{
[aViewController removeFromParentViewController];
}
}
NSMutableArray *allbViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allbViewControllers)
{
if ([aViewController isKindOfClass:[messageViewController class]])
{
[self.navigationController popToViewController:aViewController animated:NO];
}
}
The first for loop erases all the view controllers from the stack. Is there something I am doing wrong here. Please let me know.
You should not be mucking around with a navigation controller's stack of view controllers using removeFromParentViewController:. Ever. That is flat-out wrong.
Furthermore, there is no reason to loop through a navigation controller's stack of view controllers and popping them one by one. There are methods like popToViewController:animated: and popToRootViewController that let you pop to any arbitrary view controller in the stack with one call.
Have you verified that the parent view controller property is populated by the navigation controller? The parent view controller is normally used for UIViewControllerContainment protocol conformance. UINavigationController does not conform to that protocol, which makes me think you're not going to be able to remove it from the navigation controller by calling that method. You can use – setViewControllers:animated: on your navigationController to assign the controllers. Just use the array you created with all the viewControllers.
If you are using a pop, you don't have to remove the other view from it's parent view. You can just pop to a view controller that is in the view controller array and back two from the top of the stack.
I would give a code example, but i'm not on my dev computer. If nobody has answered with more detail before tomorrow, I will add some code.
-Edit - Here is the line of code that I use to pop back 2 view controllers. I'm sure this could be done better ways, but it works.
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:[self.navigationController.viewControllers count]-3] animated:YES];

popToViewController is not working "Tried to pop to a view controller that doesn't exist"

I am trying to use popToViewController and it I keep getting the error "Tried to pop to a view controller that doesn't exist"?
I am in a Settings view and when the user clicks "Sign Out" I dismiss the Settings VC and segue back to the mainView where an unwind segue method is called. In the unwind segue method I call the following.
-(IBAction)endSettingsViaLogout:(UIStoryboardSegue *)segue {
//[self performSegueWithIdentifier:#"mainToLoginSegue" sender:self];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
//[self.navigationController popViewControllerAnimated:YES];
DLog(#"User finished with search");
}
When the poptoVC is called I get the "Tried to pop to a view controller that doesn't exist".
I NSLog the self.navigationController.viewControllers and I can see the stack of VC's and the one I want to pop to is in there?
/// UPDATE //////
Ok here is what I have found. If my segue to Settings is a regular "push" segue then the code works and I get popped back to where I want. If I do a custom segue, having come from the left side of the screen then it stops working. Even with the custom segue the self.navigationcontroller.viewcontrollers shows its in the stack. So why can't I pop back to it? Or how can I pop back to it with the custom segue?
Do I get the view controller hierarchy right?
On your basis UINavigationViewController you have set a main view controller as root view controller.
Then a settings view controller has been pushed upon this.
Via "Sign out" the settings view controller is being segued back to the main view controller.
If so, you are actually trying pop "back" to a view controller, that does not exist, since you already have reached the root view controller of your navigation controller stack. In this case, all previously initialized controllers have been popped from the stack and you would have to reinitialize and push the desired view controller.
If I am missing some important point, it would be helpful, if you would describe your actual view controller stack at the moment the "Sign out" option is available. Furthermore, what exactly is printed on the console if you log the self.navigationController.viewControllers array?
well that basically tells objectatindex 1 does not exist:
things to try:
objectatindex:0
or
nslog(#"%d",[[self.navigationController.viewControllers]count]);//add it before the popline and see if it works
if its the topview controller then try below:
[navigationController topViewController] instead
NSArray *viewControllers = [[self navigationController] viewControllers];
for( int i=0;i<[viewControllers count];i++)
{
id obj=[viewControllers objectAtIndex:[viewControllers count]-i-1];
if([obj isKindOfClass:[OrderCheckOutViewController class]]){
[[self navigationController] popToViewController:obj animated:YES];
return;
}
}
you can use the snippet to pop out to targetVC's next viewController in the navigationController's stack.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
BOOL findIt = NO;
UIViewController *targetVC = nil;
for (UIViewController *subVC in self.navigationController.viewControllers) {
if (findIt) {
break;
}
if (subVC == xxx) {
findIt = YES;
}else{
targetVC = subVC;
}
}
[self.navigationController popToViewController:targetVC animated:NO];
});

Resources