I have a problem similar to this question, but with a different cause it appears.
I have A nav controller with Views A>B>C>D>E.
In C I push to D, in which users "create a new session". Sometimes users want to add detail to this new session, so they click add detail which pushes to E.
When they don't choose to add detail they press "done" on D, I call [navControler popViewControllerAnimated:YES]; and they end up in C without the problem mentioned in the question above, meaning if on C they press back, it successfully goes to B. However, when they do add detail, they go to E and when finished they press the nav ctrl's back button. At that point I added this code to take them back to C:
NSArray *allViewControllers = [[self navigationController] viewControllers];
for (UIViewController *aViewController in allViewControllers) {
if ([aViewController isKindOfClass:[TSessionMgmtViewController class]]) {
[self.navigationController popToViewController:aViewController animated:NO];
}
}
They end up back at C, but when they hit the back button on C, it does the strange back animation but ends up on C again. Then pressing back a second time takes them back to B.
I have already checked the following:
I am not calling extra notifications that cause an extra C to be pushed.
I am not pushing C in any other way
I checked the nav controller stack with NSLog(#"nav controller stack: %#", [[self navigationController] viewControllers]); and it does not show an extra C on the stack.
So is this a problem with the back button itself on C? Or is there some invisible C view controller that's there but does not show when printing the stack?
Thanks so much in advance!
You ask the nav controller for the list of vcs, but the first time you call pop, they change! You should just choose one to pop to. Or use a nav cont subclass and add logic to it so when you just pop the top controller, the one under it ( in its viewWillAppear) can query to see what it's suppose to do.
Related
I have a UITableViewController (A) listing some cities.
When the user taps on a row, he goes to the UIPageViewController (B) which dispatches several pages in a UIViewControllers (X), the form, displaying the detail of that city, e.g. population, history....
So when the user is consulting a city he can swipe horizontally to consult the previous and the next cities.
On this view (X) there is a "Show Map" button. If the user taps on this button he gets taken to the UIViewController (C) displaying the MKMapView containing the pins of ALL the cities in the list "A".
So if the user is consulting the city Boston, then he clicks on the "Show Map" button, he sees the map with all the cities, then he taps on the New York pin, I bring him back to the view B (containing X) with the API
// viewControllers: A,B,C; we go from C to B
[previousControllerB SetCity:NewYorkCityID];
[self.navigationController popViewControllerAnimated:YES];
Simple. And it works well.
On the UITableViewController A (the list) I have put a "Show Map" button too, so the user can consult the Map displaying all the city-pins without passing through the controller B. It works.
While on the map, the user clicks on the pin "New York" and I have to display the View X within the UIPageViewController B. So the user can still swipe and go forth and back with the cities.
Here comes the trouble.
If I go back to the view controller A (list) then I push the view B (form), I get a bad animation. I don't want to see the controllerA.
// viewControllers: A,C; we go from C to A then to B thanks to the segue GoToControllerB
[self.navigationController popToViewController:controllerA animated:YES]; // even animated:NO gives bad results
[controllerA performSegueWithIdentifier:#"GoToControllerB" sender:NewYorkCityID];
If I instantiate a brand new viewController B and push it, it doesn't work. The transition blocks at 50%, I see a black half-a-screen (right side) then more nothing happens.
// viewControllers: A,C; we instantiate B then we go from C to B
ViewControllerB *controllerB = [self.storyboard instantiateViewControllerWithIdentifier:#"ControllerB"];
controllerB.view = controllerB.view;
[controllerB SetCity:NewYorkCityID];
[self.navigationController pushViewController:controllerB animated:YES];
I noticed that this problem happens only if I instantiate and push a UIPageViewController, and it never happens if I instantiate and push a UIViewControllers.
So the first question is how to instantiate and push the UIPageViewController.
Then, in this latest case, since I instantiate the view B from the controller C, I get a wrong sequence of controllers (A,C,B). The right sequence should be always
A,B,C (so: list, form, map)
or
A,C (so: list, map)
in this case, if the user from C (map), clicks on a city-pin and goes to B (form), the back button here must always bring him from B to A (the list) and not to the Map (C).
So I thought the following: if the user clicks on a city-pin on the map, firstly I instantiate the viewController B (so the sequence now is A,C,B) then I remove the controller C from the self.navigationController.viewControllers.
so when the user is on B and tap on the back button he always goes to the A controller.
Could this work? How to do that? Any sample code? Thank you.
Try this for maintain your navigation sequence ...A -> B -> C
1. go to ViewController C
ViewControllerc *objvcC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerC"];
[self.navigationController pushViewController:objvcC animated:YES];
2. then add ViewController to existing navigation stack
ViewControllerB *objViewControllerB = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerB"];
NSMutableArray *existNavigationArray = [self.navigationController.viewControllers mutableCopy];
[existNavigationArray insertObject:objViewControllerB atIndex:existNavigationArray.count -1];
self.navigationController.viewControllers = existNavigationArray;
I have 3 View Controllers, conveniently named A, B, C.
A is presented using a UINavigationController, and has a push segue to C.
My desired effect is that after performing the segue, C's back button will pop us into B, and then B's back button pop us to A. Effectively, this means that segueing to C will put both B and C onto the stack.
in iOS7, I've used the following (redacted) code successfully:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
CViewController *slotViewer = segue.destinationViewController;
/* Do Stuff */
BViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"B"];
[self.navigationController pushViewController:controller animated:NO];
}
The result appearance was a smooth transition from A to C, while B is loaded and B's ViewWillAppear is only called when clicking on C's back button.
However, on iOS8, this breaks: The transition briefly flashes B on the screen, its viewWillAppear is called, and only then C is shown.
Any idiomatic way to perform this transition in iOS8?
I know I can use the method described here but it feels awkward, and I'd rather use the Segues all the way.
The method (setViewControllers) you linked was designed to do exactly you want. Why is it awkward? It's provided by Apple.. Besides that there is no way to use segues only. Your previous solution was same like pushing first one programatically and right after second one by interface builder..this is awkward man:).
Please don't do this. You're not supposed modify the navigation stack like that! It's awkward.
My Application flow is as below where A, B , c and D are view controllers.
Arrows mark presenting from and to view controllers.
Now I need home button in B , C and D view controller that navigate back to A.
I am not using storyboard.
Its I am unable to use dismissviewcontroller as it dismisses just once, where in some cases its required 2 or 3 previous view controllers dismiss.
Any suggestions in this regards will be helpful.
It sounds like you should be using A as your root view controller on a UINavigationController. The only thing B, C, and D will need to do is call popToRootViewController.
You'll need to make your login view controller be pushed from A, but you can do it without the user seeing it by putting the code in the AppDelegate (which is likely where you're checking to see if the user needs to log in anyway).
How about using setViewControllers:animated:. Where ever you are, you get the first View controller as firstViewController = [self.navigationController viewController] firstObject], then [self.navigationController setViewControllers:#[firstViewController] animated:YES].
See here: setViewControllers:animated:
i am having issues try to manipulate the navigationstack from an ios app. Or at least with the behaviour as a result of that manipulation.
My Situation:
i have 3 ViewControllers.
Controller a shows multiple levels,
Controller b is the gameview
Controller c is some sort of Score
Obviously i will select a level in controller a, which triggers a segue to controller b, once the level is finished ill segue to controller c. every segue as a push.
Now once im in controller c i dont want to be able to go back to b using the back button. I want to move back to controller a. In order for this to work, i removed the controller from the stack, so back wont move to controller b. This works fine.
The issue im facing is that the backbutton does show on controller a, which seems off since there shouldn't be any back. If i click the backbutton, the app doesnt crash, the button just disappears leaving the title.
i tried adding :
NSArray* controllers = [self.navigationController viewControllers];
if ([controllers count]<=1) {
[self.navigationItem setHidesBackButton:YES animated:YES];
} else {
[self.navigationItem setHidesBackButton:NO animated:YES];
}
[super viewDidAppear:animated];
as suggested in some relative stackoverflow article, without success. Besides this not working it seems off that ios creates those buttons from Storyboard without me actually adding them, but doesnt remove them when they arent necessary anymore. This leaves me with some options.
either i think that ios is smarter than it actually is
i am missing something essential to update the navigationbar
i went at this all wrong
besdies im using this code snipped to segue from Controller b to c.
[self performSegueWithIdentifier:#"feedbackSegue" sender:self];
[self removeFromParentViewController];
Any hints concerning missing operations or general bad practice is greatly appreciated.
Update
After further investigation, its not just the back button, its the whole navigationbar that is off. it behaves as if the removed controller was still there. The BackButton is there and another uiActionButton on the right end.
Does the navigationbar store its states onto a different stack, than the viewcontroller one? if this was the case, i could remove that state from this stack as well, to keep it consistent.
You could try this in your view controller c. This will remove the previous view controller, in your case the b. You'd also have to keep b in your stack (remove the line [self removeFromParentViewController]'; )
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if(self.navigationController.viewControllers.count>2){
NSArray *controllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:controllers];
[newViewControllers removeObject:[controllers objectAtIndex:self.navigationController.viewControllers.count - 2]];
self.navigationController.viewControllers = newViewControllers;
}
}
I believe the "correct" way to do this is to remove the back button on controller c. Depending on how and when you are removing controller b, you may be corrupting the navigation controller stack. It's generally not a good practice to manipulate the view controller stack.
To remove the back button, you have the correct code:
self.navigationItem.hidesBackButton = YES;
However, note that you must call this before the view controller is presented -- i.e., in something like viewDidLoad.
When you want to pop back to A, use:
[self.navigationController popToRootViewControllerAnimated:YES];
I'm trying to get a storyboard to bring the user back to the first screen of a storyboard when a button is pressed.
I have the button click hooked up and the method is hit when it's tapped but it's either doing nothing or crashing the app depending on what I've got in that method (I've tried so many things at this stage that I can't remember the original setup)
The best I can achieve is that once tapped the user gets brought to the third screen in the storyboard, rather than the first.
Here's the storyboard, I want to get the user to move from the button circled in red back to the very first view controller (top left of screenshot).
Maybe the storyboard layout will help people, I'll post some of the various methods I've tried as well.
Here's the method
- (IBAction)done:(id)sender
{
//Multitude of attempts have been in here, either they don't do
//anything or they just send the user back to the NEW REPORT screen.
//Button does nothing in these following events
[self.navigationController popToRootViewControllerAnimated:YES];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:0] animated:YES];
}
If you want to start fresh, have the button instantiate a new instance of the first controller, and set it as the window's root view controller:
FirstViewController *first = [self.storyboard instantiateViewControllerWithIdentifier:#"First"];
self.view.window.rootViewController = first;
It's not clear to me exactly how you're getting to that controller with the button in question, but you might want to put dealloc methods in all your controllers with a log to see if all are getting deallocated at some point in your navigation, and when you go back to the new first controller as I've outlined above.