popToViewController not animating - iphone

Man I must say if it wasn't for this site, I would have no hair and I would've probably jumped off of a bridge right now. OK! My problem:
I'm using storyboard with xcode 4.4.
I have a 3 view controllers (well 5 really but the others i THINK are irrelevant). View controller A pushes to view controller B. View controller B has a segmented control on its nav bar with 2 segments and a back button.View contoller B loads with segment 1 selected. When user selects segment 0, view controller C is then instantiated. All well and good on the first push. You get pushed to B from A, and can then when you press back it pops to A. My problem is when I popToViewController to an array list containing view controller A, it pops, but it doesnt animate! I know what you're thinking, "Animate:YES dummy!" However, animated IS yes..HUH? Here's my code:
heres whats goin on in view controller B
-(IBAction)Previous:(id)sender{
[self.navigationController popViewControllerAnimated:YES];
}
- (IBAction)segmentSwitch:(id)sender;
{
if ([sender selectedSegmentIndex] == 0)
{
MapViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"Map"];
[self.navigationController pushViewController:controller animated:NO];
}
else if ([sender selectedSegmentIndex] == 1) {
ListViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"List"];
[self.navigationController pushViewController:controller animated:NO];
}
}
heres view controller C
-(IBAction)Previous:(id)sender
{
NSInteger index = -1;
ReportAppDelegate *appDelegate =
(ReportAppDelegate *)[[UIApplication sharedApplication]delegate];
MainOrangeTestViewController *orangeViewController = [appDelegate orangeViewController];
orangeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Orange"];
NSArray* newVCarray = [[NSArray alloc] initWithObjects:orangeViewController, nil];
self.navigationController.viewControllers = newVCarray;
for(int i=0 ; i<[newVCarray count] ; i++)
{
if([[newVCarray objectAtIndex:i] isKindOfClass:NSClassFromString(#"MainOrangeTestViewController")])
{
index = i;
}
}
[self.navigationController popToViewController:[newVCarray objectAtIndex:index] animated:YES];
}
- (IBAction)segmentSwitch2:(id)sender; {
if ([sender selectedSegmentIndex] == 0)
{
MapViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"Map"];
[self.navigationController pushViewController:controller animated:NO];
}
else if ([sender selectedSegmentIndex] == 1)
{
ListViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"List"];
[self.navigationController pushViewController:controller animated:NO];
}
}
I know when you instantiate you are creating a new instance of that view controller as well as creating a new nav stack (or something like that). I'm thinking that might have something to do with it. Regardless, why on earth is it not animating the pop? I'm hoping to implement something in my current build, however if it requires a rebuild then so be it! Any help is greatly appreciated. I've definitely done my homework on this one however no solution.
Thanks to all in advance!
EDIT: I should add that the initial POP back to A from view controller B is animated. HOwever when you start to toggle the segmented controls AND when you toggle to view contrller C, going back to view controller A doesn't animate the pop. So initially when you load to B from A it pops back with successful animation. But after the toggle it pops back to A with no animation and the animated method is YES.

Are you just trying to push A->B->C and then pop back to A again, skipping B? If so, there's no need to modify the navigation controller's viewControllers property.
// in vcC.m ...
-(IBAction)Previous:(id)sender
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
Check out the different varieties of pop in the class reference. You can pop back one, or pop to the root, or pop to a particular VC that's in your history. The way to do that is to read the viewControllers stack (not write it), find the the VC you want to pop to, then use the popTo... method that's in your code.
But it sounds like popToRoot is what you'll need.

Still a little confused about your situation, but here's another tip: You can create history beforehand. Let's say you want to push from VC0 to VC2, and when popping, you want to pop from VC2 to either VC1 or to VC0. Totally doable:
// in VC0.m, animated push to VC2
// push to vc1 invisibly
[self.navigationController pushViewController:vc1 animated:NO];
// we've made history! now push visibly to vc2
[self.navigationController pushViewController:vc2 animated:YES];
To choose where to pop back...
// in VC2.m
if (/* user needs a regular pop back to VC1 */) {
[self.navigationController popViewControllerAnimated:YES];
} else // pop back two, to VC0
NSArray *viewControllers = [self.navigationController viewControllers];
// we're not assuming that vc0 is the root here, the top of the navigation stack
// is at index=count-1. self is at the top. a regular pop is just below == count-2.
// two pops is at count-3, and so on.
NSInteger backBackIndex = viewControllers.count - 3;
// in case we get here by some other pushing path, worst case pop is to the root
backBackIndex = MAX(backBackIndex, 0);
UIViewController *backBackVC = [viewControllers objectAtIndex:backBackIndex];
[self.navigationController popToViewController:backBackVC animated:YES];
}

Related

How to get a particular view controller from the stack of viewcontrollers in the navigation controller?

I have an application in which i am having a table view.when pressing a button on the cell i have to go to another view controller(ie:testviewcontroller) showing what i selected in the previous one.then when pressing one button i need to go to another view controller showing the remaing values.if he select one there then i need to repeat the above process.The problem is from here also i am carrying some values to that testviewcontroller.if i pop back to my view controller how can i carry the new values.currently i am doing like this.
TestViewController *test =[[ TestViewController alloc]initWithNibName:#"TestViewController" bundle:nil];
test.itemselected=head;
test.itemid=productid;
//NSLog(#"%#",del.navigationController);
[del.navigationController pushViewController:test animated:YES];
but i know
NSArray *array = [del.navigationController viewControllers];
[array objectAtIndex:3]is my desired view controller.
can anybody know how can i avoid this pushing of same view controller again?
for (UIViewController*vc in [self.navigationController viewControllers]) {
if ([vc isKindOfClass: [TestViewController class]]){
vc.itemselected= head ;
[[self navigationController] popToViewController:vc animated:YES];
}
}
*EDIT*
This should be
for (TestViewController*vc in [self.navigationController viewControllers])
instead of
for (UIViewController*vc in [self.navigationController viewControllers])
You can traverse loop on view controller stack.
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
NSMutableArray *ViewControllerArray=[[NSMutableArray alloc]init];
for (long i=[self.navigationController.viewControllers count]-1;i>0; i--) {
[ViewControllerArray addObject:[self.navigationController.viewControllers objectAtIndex:i]];
NSLog(#"%#",ViewControllerArray);
NSLog(#"%#",self.navigationController.viewControllers);
}
for (UIViewController *controller in self.navigationController.viewControllers) {
//Do not forget to import AnOldViewController.h
if ([controller isKindOfClass:[YourViewController class]]) {
[self.navigationController popToViewController:controller
animated:YES];
break;
}
}
}
[super viewWillDisappear:animated];
}
For your condition it is beter to pop back and reload the table with new contents..no need to push to a new instance
Use a seperate array for datasource of table
Remove the selected item from datasource array
Then push to next view when poped back reload contents

switching view black page

I'm trying to do a switch view. My first view is a storyboard and i want to switch for view2, i have no problem when click the botton in my first view everything is fine. The prolem is when i am trying to go back, the screen is going black and is not going back to the first view. Here the code that i'm using.
ViewController.h
- (IBAction)View2:(id)sender;
ViewController.m
- (IBAction)View2:(id)sender {
View2 *second = [[View2 alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
}
And here the code that i am using to go back from the second view to the first.
view2.h
- (IBAction)back:(id)sender;
View2.m
- (IBAction)back:(id)sender {
ViewController *second = [[ViewController2 alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
}
Am I doing some kind of error?
Thanks
Your issue is in View2.m
- (IBAction)back:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
should do the trick
Apple has a View Controller Programming Guide that I suggest skimming through
UPDATE
If you need to jump back more that one ViewController, you should use NSNotificationCenter or keep a reference to the viewController you need to jump back to.
In my experiences, the following line does 1 of 2 things (where self is a subclass of UIViewController)
[self dismissModalViewControllerAnimated:YES];
1) If self has presented another view controller, the line above will dismiss all view controllers that have been presented on top of self
2) If self has not presented any other view controller, the line above will dismiss self back to the previous view controller

popToViewController, then pushViewController

After my rootViewController, there is a workflow that would go like this
viewController1 -> push viewController2 -> push viewController3 -> ***** viewController2
I would like to popToViewController3. However, I need to go to viewController1 first because viewController1 basically determines which instance of viewController2 to push. So I
// pseudo code
[popToViewController:vc1 animated:NO];
[self.navigationController pushViewController2 animated:YES];
I basically pop before pushing so I don't end up getting vc2, vc3, vc2, vc3, vc2 etc. But in doing this, since it animates the push of the last viewController, it gives the user the impression that a new viewController was pushed onto the stack, vs popping to the previous viewController. Is there a way to get around this ? If I animate the pop and not the last push, I do not get the correct viewController I want on top by the looks of it. Thanks.
can you use something like
[self.navigationController setViewControllers:[NSArray arrayWithObject:yourViewController]
animated:YES];
you can fill the array with the view controllers you want on the stack.
#interface UINavigationController (PushThenPop)
-(void)popToViewController:(UIViewController*)rootViewController
thenPushViewController:(UIViewController*)pushViewController;
#end
#implementation UINavigationController (PushThenPop)
-(void)popToViewController:(UIViewController*)rootViewController
thenPushViewController:(UIViewController*)pushViewController
{
NSMutableArray *viewControllers = self.viewControllers.mutableCopy;
// Pop viewcontrollers until we reach the rootViewController
while(viewControllers.count >= 1) {
id last = [viewControllers lastObject];
if (last == rootViewController) break;
[viewControllers removeLastObject];
}
// Bail out the array is empty. We expect the array to contain at least 1 element, the rootViewController.
if (!viewControllers.count) {
return;
}
// Push the new view controller
[viewControllers addObject:pushViewController];
self.viewControllers = viewControllers.copy;
}
#end
You can retrieve (viewControllers), modify, and "reinstall" (setViewControllers) the list of view controllers managed by the navigation controller, and the "reinstall" can (in theory) be animated. (However, I had some problems with animation in practice, though I don't recall the details.)

Return to the first index of UINavigationController?

I'm doing an application which uses a UINavigationController and I'm switching to other UIViewControllers as follows:
if(self.myViewController == nil){
MyViewController *aViewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
self.myViewController = aViewController;
[aViewController release];
}
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[delegate.myNavController pushViewController:myViewController animated:YES];
I imagine this is creating a pile of UIViewControllers into the UINavigationController, maybe an array of indexs? I would like to know how to turn back without having to be back one by one.
For example, I'm sailing through a few screens and with a button I would like to return at the first index of navigation. I would also like know how to modify indexes, view, erase and anything pertaining to this issue.
Sorry if I have not explained well.
You've asked two questions.
The first is, how do I get back to my first view controller. As #Thomas Clayson and #ender have answered, you want the popToRootViewControllerAnimated: method of your navigationcontroller object for that.
The second is how to move to a particular index in the view controller stack. The answer to that is, you can set the array of viewControllers explicitly. So you can pull out the current listing of view controllers, modify it, and set it back into the navigationController stack. It'll reset the stack and animate you moving to the top item in the stack.
Thusly:
NSMutableArray *controllers = self.navigationController.viewControllers;
[controllers removeObjectAtIndex:[controllers count] - 1]; //or whatever
[self.navigationController setViewControllers:controllers animated:YES];
NSArray *viewControllers = [[self navigationController] viewControllers];
for (int i = 0; i < [viewContrlls count]; i++){
id obj = [viewControllers objectAtIndex:i];
if ([obj isKindOfClass:[yourViewControllername class]]){
[[self navigationController] popToViewController:obj animated:YES];
return;
}
}
Using this you can come back to any specified viewController.
[self.navigationController popToRootViewControllerAnimated:YES];
Will take you back to the very first view controller (root view controller).
Hope this helps
Use this
NSArray *viewContrlls=[[NSArray alloc] initWithArray:[[self navigationController] viewControllers]];
id obj=[viewContrlls objectAtIndex:1];
[[self navigationController] popToViewController:obj animated:YES];
[viewContrlls release];
You should use popToRootViewControllerAnimated: From UINavigationController class reference:
Pops all the view controllers on the stack except the root view
controller and updates the display.
You can return to the first view with
[self.navigationController popToRootViewControllerAnimated:YES];
That being said, you can also remove a particular view controller, or navigate to a specific index in your view controller if you look at the example.
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:navigationController.viewControllers];
// You can now manipulate this array with the methods used for NSMutableArray to find out / perform actions on the navigation stack
[allViewControllers removeObjectIdenticalTo: removedViewController];
// You can remove a specific view controller with this.
navigationController.viewControllers = allViewControllers;

How do I pop the view controller underneath a pushed view controller?

I want to push a view controller onto the stack, then pop the first one that pushed the new one.
-(void) someMethod {
MegaSuperAwesomeViewController *tempVC = [[MegaSuperAwesomeViewController alloc] init];
[self.navigationController pushViewController:tempVC animated:YES];
[tempVC release];
// pop this VC, how?
}
EDIT: turns out I can pop back 2 view controllers instead once finished with the new VC. Still not what I wanted exactly, but it works. The downside is I need to set a flag to indicate that the covered view is completed.
Here's a technique of popping back two view controllers, which has a similar problem of yours of the current view controller and its navigationController property going away as soon as you do the first pop:
// pop back 2 controllers on the stack to the setup screen
//
// locally store the navigation controller since
// self.navigationController will be nil once we are popped
//
UINavigationController *navController = self.navigationController;
// retain ourselves so that the controller will still exist once it's popped off
//
[[self retain] autorelease];
// Pop back 2 controllers to the setup screen
//
[navController popViewControllerAnimated:NO];
[navController popViewControllerAnimated:YES];
alternatively, you can directly "party" on the navigation controllers stack of view controllers:
setViewControllers:animated: Replaces
the view controllers currently managed
by the navigation controller with the
specified items.
(void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated Parameters
viewControllers The view controllers
to place in the stack. The
front-to-back order of the controllers
in this array represents the new
bottom-to-top order of the controllers
in the navigation stack. Thus, the
last item added to the array becomes
the top item of the navigation stack.
animated If YES, animate the pushing
or popping of the top view controller.
If NO, replace the view controllers
without any animations. Discussion You
can use this method to update or
replace the current view controller
stack without pushing or popping each
controller explicitly. In addition,
this method lets you update the set of
controllers without animating the
changes, which might be appropriate at
launch time when you want to return
the navigation controller to a
previous state.
If animations are enabled, this method
decides which type of transition to
perform based on whether the last item
in the items array is already in the
navigation stack. If the view
controller is currently in the stack,
but is not the topmost item, this
method uses a pop transition; if it is
the topmost item, no transition is
performed. If the view controller is
not on the stack, this method uses a
push transition. Only one transition
is performed, but when that transition
finishes, the entire contents of the
stack are replaced with the new view
controllers. For example, if
controllers A, B, and C are on the
stack and you set controllers D, A,
and B, this method uses a pop
transition and the resulting stack
contains the controllers D, A, and B.
Availability Available in iOS 3.0 and
later. Declared In
UINavigationController.h
So, to "disappear" the view controller directly under you on the navigation stack, in your view controller's viewDidLoad, you could do this:
NSMutableArray *VCs = [self.navigationController.viewControllers mutableCopy];
[VCs removeObjectAtIndex:[VCs count] - 2];
self.navigationController.viewControllers = VCs;
I had trouble figuring this out also so I wanted to share how I got this to work.
Let's say you have a stack of VCs VC1 being the root then you push VC2 and from VC2 you want to push VC3 but once pushed you don't want the user to go back to VC2 but rather to VC1 (the root). The way to do that is:
//push VC3 from VC2
[[self navigationController] pushViewController:VC3 animated:YES];
// now remove VC2 from the view controllers array so we will jump straight back to VC1
NSMutableArray *viewHeirarchy =[[NSMutableArray alloc] initWithArray:[self.navigationController viewControllers]];
[viewHeirarchy removeObject:self];
self.navigationController.viewControllers = viewHeirarchy;
Hope this helps someone else
Thanks Bogatyr about the tip on 'party on the viewcontroller array for the navcontroller'. I just replaced the entire stack with the one viewcontroller I want to change to, and then log out all the viewcontrollers in the stack to make sure its the only one! Worked great - thanks!
RatingsTableViewController *newViewController = [[RatingsTableViewController alloc] init];
NSMutableArray * newVCarray = [NSMutableArray arrayWithObjects:newViewController, nil];
self.navigationController.viewControllers = newVCarray;
[newViewController release];
NSMutableArray *allControllers = [[NSMutableArray alloc] initWithArray:self.navigationController.viewControllers];
for (id object in allControllers) {
NSLog(#"name VC: %#", object);
}
[allControllers release];
-(void)popToSelf{
NSArray *array = [self.navigationController viewControllers];
for (int i = 0 ; i < array.count ; i++) {
UIViewController *currentVC = [array objectAtIndex:i];
if ([currentVC isKindOfClass:[YourViewControllerClass class]]) {
[self.navigationController popToViewController:[array objectAtIndex:i] animated:YES];
}
}
}