Removing ViewController from navigation stack - ios

I do it with code:
NSArray *viewControllersFromStack = [self.navigationController viewControllers];
NSMutableArray *viewControllersFromStackMutable = [NSMutableArray arrayWithArray:viewControllersFromStack];
NSMutableArray *viewControllersToRemove = [[NSMutableArray alloc]init];
for (UIViewController *currentVC in viewControllersFromStack)
{
if ([currentVC isKindOfClass:[TalksViewController class]])
{
[viewControllersToRemove addObject:currentVC];
if (viewControllersToRemove.count == 2)
{
UIViewController *oneVCtoRemove = [viewControllersToRemove objectAtIndex:0];
[viewControllersFromStackMutable removeObject:oneVCtoRemove];
[self.navigationController setViewControllers:viewControllersFromStackMutable];
}
}
}
Problem is that I have reference to removed VC's in navigation Item. How to fix it?

When you want to remove a view from the navigation stack you can simply just call this method on the navigation bar to pop the view from the stack:
[self.navigationController popViewControllerAnimated:YES];
To pop an external view use
for(UIViewController *currentVC in viewControllersFromStack)
{
if([currentVC isKindOfClass:[TalksViewController class]])
{
[currentVC.navigationController popViewControllerAnimated:YES];
}
}

The above answer is correct.
I have 'A' as rootview controller. 'B to F' are other view controllers. From 'F', if I wanted to go directly to 'A', it is as under.
[self.navigationController popToRootViewControllerAnimated:YES];
BUT if I wanted to jump to 'B' then the code in answer is helpful. I only changed the array of view controllers to run reverse with 'reverseObjectEnumerator' and Animated to NO with 'popViewControllerAnimated:NO'. the Code is as under
NSArray *viewControllersFromStack = [self.navigationController viewControllers];
for(UIViewController *currentVC in [viewControllersFromStack reverseObjectEnumerator])
{
if(![currentVC isKindOfClass:[A class]] && ![currentVC isKindOfClass:[B class]])
{
[currentVC.navigationController popViewControllerAnimated:NO];
}
}

Related

Pushing and removeFromParentControllers, Affected the structure of navigation controller

enter image description here
Image on above link, cant include it in the post..
As I push from viewControllerA to B, then I push from B to C, then I use the following code to remove the viewControllerB and push C to B and remove viewControllerC:
NSMutableArray *navigationArray = [[NSMutableArray alloc] initWithArray: self.navigationController.viewControllers];
for(UIViewController *tempVC in navigationArray)
{
if([tempVC isKindOfClass:[viewControllerB class]])
{
[tempVC removeFromParentViewController];
}
}
[self performSegueWithIdentifier:#"toViewControllerB" sender:self];
for(UIViewController *tempVC in navigationArray)
{
if([tempVC isKindOfClass:[viewControllerC class]])
{
[tempVC removeFromParentViewController];
}
}
So now ViewControllerA is connected to the last ViewControllerB
After that I still get the back button in the navigation bar, but after I pressed the back button of it, the ViewControllerB popped back to ViewControllerA, but in the ViewControllerA, the navigation bar items and title are gone, I purposely push from C to B again to fix some memory issue, how can I make this work by taking out the two view controllers in the middle but not messing up the navigation controller? Is there any other alternatives to get this done? Thank you
I have another weird approach is to programatically create a navigation bar with the same items and title to cover it, but just seems so wrong...
First when you want to push from B to C and then remove B, you can try this
for(UIViewController *tempVC in navigationArray)
{
if([tempVC isKindOfClass:[viewControllerB class]])
{
[navigationArray removeObject:tempVC];
break;
}
}
[self.navigationController setViewControllers:navigationArray];
[self performSegueWithIdentifier:#"toViewControllerB" sender:self];
But you must ensure the segue is from C to B. Then you will get the Controllers A->C->B. If you want to push to ControllerC and remove Last C. Try to use the same function.
1) When you first push to your C controller from B then first Remove B controller
2) When you push to B controller from C controller then remove C controller.
// Code for removing C Controller from stack
NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *viewControllersMutable = [NSMutableArray arrayWithArray:viewControllers];
for (UIViewController *vc in viewControllers) {
if (([viewControllers indexOfObject:vc] != viewControllers.count - 1 ) && [vc isKindOfClass: C.class] ){
NSLog(#"removing contoller of kind C");
[viewControllersMutable removeObject:vc];
}
}
self.navigationController.viewControllers = viewControllersMutable.copy;
// Code for removing B controller
NSArray *viewControllers = self.navigationController.viewControllers;
NSMutableArray *viewControllersMutable = [NSMutableArray arrayWithArray:viewControllers];
for (UIViewController *vc in viewControllers) {
if (([viewControllers indexOfObject:vc] != viewControllers.count - 1 ) && [vc isKindOfClass: B.class] ){
NSLog(#"removing contoller of kind B");
[viewControllersMutable removeObject:vc];
}
}
self.navigationController.viewControllers = viewControllersMutable.copy;
Note -> You can do this in a same code Only "OR" condition is checked, means the controller is B or C.

How to set segue doing pop to root and push a new one in storyboard?

I want to make a segue that makes navigation controller poptoroot and then make a new controller pushed into the navigation controller .
e.g Now the controller hierarchy is
navigationController->rootcontroller->controllerA ->controllerB
and i want to make a segue perfome in controller B,and the segue will make the hierarchy to
navigationController->rootcontroller-> controllerC
is this possible?
You want to set segue doing pop to root. This can be achieved using Unwind Segues. For more detail regarding this you can follow this Apple doc.
https://developer.apple.com/library/content/technotes/tn2298/_index.html#//apple_ref/doc/uid/DTS40013591-CH1-TNTAG2-ADDING_AN_UNWIND_SEGUE_TO_A_STORYBOARD
For pushing to new VC, You can simply push in storyboard.
You can use for loop to pop till root or where you want and use same method to push forward:
// For Pop
NSArray *viewControllers = [[self navigationController] viewControllers];
for( int i=0;i<[viewControllers count];i++){
id obj=[viewControllers objectAtIndex:i];
if([obj isKindOfClass:[ViewControllerYouWantToStopOn class]]){
[[self navigationController] popToViewController:obj animated:YES];
return;
}
}
//For Push
NSArray *viewControllers = [[self navigationController] viewControllers];
for( int i=0;i<[viewControllers count];i++){
id obj=[viewControllers objectAtIndex:i];
if([obj isKindOfClass:[ViewControllerYouWantToJumpOn class]]){
[[self navigationController] pushViewController:obj animated:YES];
return;
}
}
This code work for me :
self.performSegue(withIdentifier: "segue_id", sender: self)
var navigationArray = self.navigationController?.viewControllers
var i = navigationArray!.count - 2
while i > 0 {
navigationArray!.remove(at: i)
i = i - 1
}
self.navigationController?.viewControllers = navigationArray!

iOS - a questions about present and push the controllers

There are five controllers here, AViewController, BViewController, CViewController,DViewController,EViewController,controllers here,
A present---> B
B present---> C
C push--->D
D push--->E
Now, if I want to go back from EViewController to AViewController in one step, what code should I write?
[self.navigationController popToRootViewControllerAnimated:animated];
1) Getting the desirable ViewController as Below
for (id controller in [self.navigationController viewControllers])
{
if ([controller isKindOfClass:[AViewController class]])
{
[self.navigationController popToViewController:controller animated:YES];
break;
}
}
2) Here you have A,B,C,D,E Controllers. means A would be on 1 Position so what can you do
you can hard wired the Index as Below
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:0] animated:YES];
3) Pop to the first viewController or rootViewController
[self.navigationController popToRootViewControllerAnimated:animated];
Use unwind segues.
In AViewController add bellow code
-(IBAction)prepareForUnwind:(UIStoryboardSegue *)segue {
}
Go to User Interface of EViewController and Ctrl-drag from the button (you want set action) to the “Exit” outlet, you will see a modal popup.
You can do it recursively with generic solution. First of all you should have reference to A's navigation controller and then you should write recursive method to get active navigation controller like :`
-(UINavigationController*)getActiveNavigationController : (UINavigationController*)navigationController {
if ([navigationController.presentedViewController isKindOfClass:[AViewController class]]) {
return [self getActiveNavigationController:(UINavigationController*)((AViewController*)navigationController.presentedViewController)
];
}
if ((UINavigationController*)navigationController.presentedViewController == nil) {
return navigationController;
}
return [self getActiveNavigationController:(UINavigationController*)navigationController.presentedViewController];
}
`
After that you should write method like
-(void)getInitialScreen:(UINavigationController*)AViewControllerNavigationController {
if ([AViewControllerNavigationController.presentedViewController isKindOfClass:[AViewController class]]) {
return;
}
UINavigationController *navigation = [self getActiveNavigationController:AViewControllerNavigationController];
[navigation dismissViewControllerAnimated:YES completion:^{
[self getInitialScreen:AViewControllerNavigationController];
}];
}
finally after you wrote those 2 methods. You can call them like below and you can always get AViewController
[self getInitialScreen:AViewControlelrnavigationcontroller];
[AViewControlelrnavigationcontroller popToRootViewControllerAnimated:YES];

Best practice for pushing a viewController in UINavigationController stack

Sorry if this question was asked prior.
I wanted to know how do we handle navigationController stack like am looking for best practices
for now what i am doing is I check if a viewcontroller is already present and then in the navigation stack and then i pop it back by doing something like this
bool flag = NO;
for(LoginViewController *vc in self.navigationController.viewControllers)
{
if ([vc isKindOfClass:[LoginViewController class]]) {
flag = YES;
}
}
if (flag!=YES ){
LoginViewController *objVC = [[LoginViewController alloc]init];
[self.navigationController pushViewController:objVC animated:YES];
objVC = nil;
}else
{
[self.navigationController popViewControllerAnimated:YES];
}
Do i really need to check if any viewcontroller is already present in the navigation stack prior pushing it or does the navigation controller takes care of it and at sometime removes the viewcontroller which are not in use. Is there a better way to do this or am i already doing the right thing
In short how we can stop a viewcontroller from being pushed into the navigation stack twice.
popViewControllerAnimated will always pop the top most VC on the stack. It operates in LIFO fashion.
LoginViewController may not necessarily be the top most element on the stack hence in order to remove it you have to modify code this way.
LoginViewController *loginVC = nil;
NSUInteger index = [self.navigationController.viewControllers indexOfObjectPassingTest:^BOOL (id obj, NSUInteger idx, BOOL *stop) {
return [obj isKindOfClass:[LoginViewController class]];
}];
if (index != NSNotFound) {
//Found VC on stack
//Get LoginViewController instance
loginVC = [myArray objectAtIndex:index];
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
//Remove LoginViewController obj from stack
[viewControllers removeObjectIdenticalTo:loginVC];
//Reassign new array which does not contain LoginViewController
navigationController.viewControllers = viewControllers;
}
else {
//VC not found on stack
LoginViewController *objVC = [[LoginViewController alloc]init];
[self.navigationController pushViewController:objVC animated:YES];
}
Hope that helps!

Pop To RootView Controller issue

In my project I have some set of view controller added in a navigation controller like.
UIViewController1,UIViewController2,UIViewController3,UIViewController4,UIViewController5
consider UIViewController1 is my root view controller of navigation controller. after the navigation reaches UIViewContoller5 on button click I need to return back to my UIViewController1. So I'm writing following code.
- (void)popToRootViewControllerAnimated
{
NSLog(#"%#",[self.navigationController viewControllers]);
[self.navigationController popToRootViewControllerAnimated:NO];
}
In console it prints like
(
"<UIViewController1: 0x8e3fcc0>",
"<UIViewController2: 0x9a5d310>",
"<UIViewController3: 0x9a67b00>",
"<UIViewController4: 0x9162a00>",
"<UIViewController5: 0x9a84380>"
)
But after it finishes execution my view stays at UIViewController3. If I print [self.navigationController viewControllers] in my UIViewController3 it shows like,
(
"<UIViewController1: 0x8e3fcc0>",
"<UIViewController3: 0x9a67b00>",
)
What I m missing. Thanks in advance. Any help would be appreciated.
Try this:
UIViewController *firstVc = [viewControllers objectAtIndex:0];
[navCtrl setViewControllers:#[firstVc] animated:NO];
Try this hope this will solve your problem
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];
this controller take you at zero(0) index of you all controllers.
Try this one
UIViewController *ctrl = [self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count -1];
[self.navigationController popToViewController:ctrl animated:YES];
Hope this will solve your issue.
Have you tried this.
-(void)backButtonAction
{
YourAppDelegate *app=(YourAppDelegate *)[[UIApplication sharedApplication]delegate];
for(UIViewController *vc in app.yourNavigationController.viewControllers)
{
if([vc isKindOfClass:[UIViewController1 class]])
{
[app.yourNavigationController popToViewController:vc animated:YES];
}
}
}
This solution works
for(UIViewController *objUIViewController in [self.navigationController viewControllers])
{
if([objUIViewController isKindOfClass:[UIViewController1 class]])
{
[self.navigationController setViewControllers:[NSArray arrayWithObject:objUIViewController]animated:YES];
}
}

Resources