I use this code:
MainViewController *mvc = [[MainViewController alloc] init];
[self.navigationController popToViewController:mvc animated:YES];
and it crash and I don't know why or where I am wrong. The error is:
reason: 'Tried to pop to a view controller that doesn't exist.' , but my view controller exists.
If somebody can help me..
I would recommend writing a method similar to this in the view controller you are popping.
- (void) popCurrentViewController
{
UIViewController *popDestination = nil;
for (UIViewController *viewController in self.navigationController.viewControllers)
{
if ([viewController isKindOfClass:[MainViewController class]])
{
popDestination = viewController;
}
}
if (popDestination)
{
NSLog(#"%# Popping...", self);
[self.navigationController popToViewController:popDestination animated:true];
}
else
{
// Handle if it can not find the specific view controller.
}
}
Related
I am debugging legacy code which is always fun. The old code tried to mock the splitView delegate methods, causing all sorts of issues - mainly crashing: on a Plus device in Portrait, rotating to landscape caused the crash - if there was no detail view set, old code attempted to create one in a dodgy hack and it was just useless...
My app is UISplitViewController based, where I have a navigation stack in both master and detail sides of the splitView.
By reading though SO and using this example and was able to implement UISplitViewController delegate methods and everything is working correctly in regards to rotation, and showing the correct master/detail views when appropriate. Here is my implementation: (apologies for wall of code snippets)
- (BOOL)splitViewController:(UISplitViewController *)splitViewController collapseSecondaryViewController:(UIViewController *)secondaryViewController ontoPrimaryViewController:(UIViewController *)primaryViewController {
if ([secondaryViewController isKindOfClass:[UINavigationController class]]
&& [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[AECourseHTMLTableViewController class]]
&& ([(AECourseHTMLTableViewController *)[(UINavigationController *)secondaryViewController topViewController] htmlContentEntry] == nil)) {
// If the detail controller doesn't have an item, display the primary view controller instead
return YES;
}
return NO;
}
And the other splitView delegate method - see comments in code for where I'm stuck.
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController {
// If detail view already exists
if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
if ([controller isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)controller visibleViewController] isKindOfClass:[AECourseHTMLTableViewController class]]) {
return controller;
}
}
}
// Create detail view
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"CourseHTMLNav"];
if ([navController.viewControllers.firstObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
AECourseHTMLTableViewController *courseViewController = navController.viewControllers.firstObject;
[self configureViewController:courseViewController entry:self.contentSection.sections[0] indexPath:courseViewController.currentIndexPath];
}
// Enable back button
UIViewController *controller = [navController visibleViewController];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
if (!self.splitViewController.isCollapsed) {
UINavigationController *navController = self.splitViewController.viewControllers.firstObject;
AEContentMenuTableViewController *contentMenuVC = navController.viewControllers.firstObject; // This controller needs to be master in Landscape
NSMutableArray<UIViewController *> *controllers = [navController.viewControllers mutableCopy]; // Contains 3 controllers, first needs removed
NSMutableArray *toDelete = [NSMutableArray new];
for (UIViewController *viewController in controllers)
if ([viewController isKindOfClass:[contentMenuVC class]] || [viewController isKindOfClass:[AECourseHTMLTableViewController class]]) {
[toDelete addObject:viewController]; // Remove first VC, so master should become AEContentMenuVC?
break;
}
// Remove the object
[controllers removeObjectsInArray:toDelete];
// Set viewControllers
navController.viewControllers = controllers;
}
return navController;
}
AECourseHTMLTableViewController has next/prev buttons to select the next row in the tableview of the tableview menu class class (AEContentMenuTableViewController). I have a delegate function which can tell me the current indexPath in which AECourseHTML... is using from AEContentMenu..., and when calling it, it selects the menu tableview row and instantiates a new AECourseHTML... and pushes it.
This is where I'm stuck. In Portrait, pressing next/prev is fine, it selects the correct row and works as expected. But once I rotate the device, both master and detail views show the detail view. I can press "Back" on the master view, and it takes me to the correct AEContentMenu... class. As noted in the code snippet comments, I need to remove a ViewController from the master stack (the first object actually), and AEContentMenu... should become the first object of that stack - so when rotating, that should be the master view.
Apologies for such a long post, I've been banging my head with this for weeks now and I want to include as much info as possible in this question. Thanks in advance.
I found a solution which works well for my use cases. It may not be the cleanest code, but I'm happy with what I've got.
splitViewController:collapseSecondaryViewController:ontoPrimaryViewController:
remains unchanged. I have updated my splitViewController:separateSecondaryViewControllerFromPrimaryViewController: delegate method with the solution. Any feedback is welcome.
- (UIViewController *)splitViewController:(UISplitViewController *)splitViewController separateSecondaryViewControllerFromPrimaryViewController:(UIViewController *)primaryViewController {
// If detail view already exists
if ([primaryViewController isKindOfClass:[UINavigationController class]]) {
for (UIViewController *controller in [(UINavigationController *)primaryViewController viewControllers]) {
if ([controller isKindOfClass:[UINavigationController class]] && [[(UINavigationController *)controller visibleViewController] isKindOfClass:[AECourseHTMLTableViewController class]]) {
return controller;
}
}
}
// Return CourseVC
UINavigationController *navController = splitViewController.viewControllers.firstObject;
UIViewController *viewController;
for (viewController in navController.viewControllers) {
if ([navController.viewControllers.lastObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
return viewController;
} else {
// Create detail view
UINavigationController *navController = [self.storyboard instantiateViewControllerWithIdentifier:#"CourseHTMLNav"];
if ([navController.viewControllers.firstObject isKindOfClass:[AECourseHTMLTableViewController class]]) {
// Enable back button
UIViewController *controller = [navController visibleViewController];
controller.navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;
controller.navigationItem.leftItemsSupplementBackButton = YES;
AECourseHTMLTableViewController *courseViewController = navController.viewControllers.firstObject;
// If next/prev has been tapped, configure current ContentHTML
if (self.currentContentHTML) {
[self configureViewController:courseViewController entry:self.currentContentHTML indexPath:courseViewController.currentIndexPath];
} else {
// Create new ContentHTML from first row of AEContentMenuVC
[self configureViewController:courseViewController entry:self.contentSection.sections[0] indexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
}
return navController;
}
}
}
return navController;
}
Your top if statement should return nil. Since you were returning the nested navigation controller you were missing out on the default behaviour of popping the master navigation's top controller which is required so it can then be placed on the right.
The default behaviour will find that nested nav controller and pop it. However the reason you still need to search for it yourself is if it isn't there then you need to load the detail nav from the storyboard as you have done.
I know this question have been answered before, but i have difficulty to pop on specific page in ios. As i have already tried many times. as i have build 4 pages and and want to move last page to second page.
with the help of this code i have gone through first page
[self.navigationController popToRootViewControllerAnimated:YES];
and with the help of this page i have gone to 3rd page.
[self.navigationController popViewControllerAnimated:YES];
but for second page i have written this code
but it's not working
ViewPage2 *ptwo=[[ViewPage2 alloc] initWithNibName:#"ViewPage2" bundle:nil];
[self.navigationController popToViewController:ptwo animated:YES];
It is because you create new view controller which is not in the stack. What you should do is following:
for (UIViewController *vc in self.navigationController.viewControllers) {
if ( [vc isKindOfClass:ViewPage2]){
[self.navigationController popToViewController:vc animated:true];
return; //optional
}
}
ViewPage2 *ptwo;
for (UIViewController *vc in self.navigationController.viewControllers) {
if ([vc isKindOfClass:[ViewPage2 class]]) {
ptwo = (ViewPage2 *)vc;
break;
}
}
[self.navigationController popToViewController:ptwo animated:true];
Here is the simple answer:
NSArray *arr = [self.navigationController viewControllers];
[self.navigationController popToViewController:[arr objectAtIndex:0] animated:YES];
In place of,
[arr objectAtIndex:0]
here you can give index of specified view controller that you need to pop.
You can try doing this
first import the ViewController on which page u want to go.
like
#import ViewController.h
Then u can do the following
instantiate the class
ViewController *controller = [[ViewController alloc ] init];
[self.navigationController pushViewController:controller animated:YES];
or you can do popToViewController too
for (UIViewController *controller in self.navigationController.viewControllers) {
//Do not forget to import YourViewController.h
if ([controller isKindOfClass:[ViewController class]]) {
[self.navigationController popToViewController:controller
animated:YES];
break;
}
}
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];
}
}
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];
}
}
I have a come across a piece of code to pop to a specific viewcontroller in a navigation stack as below
for (UIViewController* viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[MyGroupViewController class]] ) {
MyGroupViewController *groupViewController = (MyGroupViewController*)viewController;
[self.navigationController popToViewController:groupViewController animated:YES];
}
}
The objective is to pop to MyGroupViewController. But I am not understanding this line of code.
MyGroupViewController *groupViewController = (MyGroupViewController*)viewController;
Whats exactly happening here. I don't think a new instance of MyGroupViewController is being created here.
//This for loop iterates through all the view controllers in navigation stack.
for (UIViewController* viewController in self.navigationController.viewControllers) {
//This if condition checks whether the viewController's class is MyGroupViewController
// if true that means its the MyGroupViewController (which has been pushed at some point)
if ([viewController isKindOfClass:[MyGroupViewController class]] ) {
// Here viewController is a reference of UIViewController base class of MyGroupViewController
// but viewController holds MyGroupViewController object so we can type cast it here
MyGroupViewController *groupViewController = (MyGroupViewController*)viewController;
[self.navigationController popToViewController:groupViewController animated:YES];
}
}
Also you can do like this
if ([viewController isKindOfClass:[MyGroupViewController class]] ) {
[self.navigationController popToViewController:viewController animated:YES];
}
Swift code
//Itrate through all the view controllers in navigation stack
for vc in self.navigationController!.viewControllers {
// Check if the view controller is of MyGroupViewController type
if let myGropupVC = vc as? MyGroupViewController {
self.navigationController?.popToViewController(myGropupVC, animated: true)
}
}
The view controllers of a navigation controller stack are being enumerated. Since these view controllers can be of any kind (but will always inherit from UIViewController), the generic UIViewController is used. However, the compiler will not know what type that view controller is, so it is being casted to a MyGroupViewController. When that happens, the compiler knows what the type of class and you can send it messages that only apply to that class.
In this case it is kind of unnecessary, as it could be simplified to this:
(UIViewController* viewController in self.navigationController.viewControllers) {
if ([viewController isKindOfClass:[MyGroupViewController class]] ) {
[self.navigationController popToViewController:viewController animated:YES];
}
}
Short answer: it changes a variable type to the type specified in the parentheses to avoid compiler warnings.
for (int m=0; m<[self.navigationController.viewControllers count]; m++) {
if([[self.navigationController.viewControllers objectAtIndex:m] isKindOfClass:[MyGroupViewController class]]) {
MyGroupViewController * groupViewController = [self.navigationController.viewControllers objectAtIndex:m];
[self.navigationController popToViewController:groupViewController animated:YES];
}
}
- (void) RetunToSpecificViewController{
for (UIViewController *controller in self.navigationController.viewControllers) {
if ([controller isKindOfClass:[AnOldViewController class]]) {
//Do not forget to import AnOldViewController.h
[self.navigationController popToViewController:controller
animated:YES];
break;
}
}
On Swift
func RetunToSpecificViewController()
{
let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController]
self.navigationController!.popToViewController(viewControllers[viewControllers.count
- 5], animated: true)
}
We have write a better tutorial on that , You can check
https://appengineer.in/2014/03/13/pop-to-specific-view-controller-in-ios/