How to navigate through 3 crossing view controllers - ios

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;

Related

How to close a dialog window in Objective C

I have viewcontroller A and on click of button, i have opened a another viewcontroller with presentation style as Form sheet. In second view controller which is opened have two buttons. Now, when these buttons are clicked, this second view controller should go away and next view controller should open on top of view controller A. But when i am trying below, another view controllers are opening up inside the dialog
i don't know how to unwind this segue and call another scene together.
Nitya, did you try the following piece of code to dismiss current view controller. Use it in the IBAction of the button where you want to dismiss the viewController:-
self.dismissViewControllerAnimated(true, completion: nil)
So you have a root viewcontroller A with a button. If you tap that button, an other viewcontroller (let's name it B) is presented by A as a form sheet. B has two buttons and if you tap one of those buttons, B should be dismissed and after a different viewcontroller (let's say C) shold be presented by A.
Apple don't really recommend this kind of workflow, nevertheless it is possible to implement it. You may shold consider to use navigation controller or master-detail controller instead of this. But if you stick to your original idea here are some possible implementations:
1.
If you are using a storyboard and the button on B triggers an unwind segue to A then you can add this to the end of the unwind seque action:
dispatch_async(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"segueThatPresentsC" sender:nil];
});
2.
If you present your viewcontrollers programmatically then:
Before you present B from A you can configure a button of B so that ist target is A and and its action is a method defined in A:
- (void) dismissAndPresentC {
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:instanceOfC animated:YES completion:nil];
}];
}
This second is maybe a bit smaller hack, but still a confusing concept.

iOS Storyboards: How to segue to the second view with a back button to the first view, but without displaying the first view

Here is my storyboard configuration:
Navigation Controller -> View Controller A -> Push-> View Controller B
^
|
Modal
^
|
View Controller C
What I want to achieve: When a button is pressed in View C, directly View B will be opened modally (No part of View A is to be displayed). Also, View B will have a navigation back button to View A.
To achieve this,
I set up the illustrated storyboard.
I created a segue between View C and the Navigation Controller of View A/B.
In the 'prepareForSegue' method of View Controller C, I get an instance of View Controller A as the first element in the navigation. In this instance, I set a variable like 'directlyProceedToViewB=YES'.
In the viewDidLoad method of View Controller A, I check the variable 'directlyProceedToViewB' and if it is YES, I call 'performSegueWithIdentifier' to segue to View B
The result is so that, first View A is opened modally and after displaying it a very short time, View B is opened with a push animation (View B can navigate back to View A, which is good). But I do not want View A to be displayed any time at all. How can I achieve this?
EDIT:
To better visualize, I'm adding a screenshot with more example cases to support:
Here are some cases I want to support:
We can start with ViewC, click on 'Modally Display B' which opens ViewB, then click 'Back to A' to navigate back to ViewA, then click on 'Dismiss Modal' on ViewA to go back to ViewC
We can start with ViewD, clcik on 'Modally Display A' which opens ViewA, then click on 'PushB' to open ViewB, then go back and forth between A and B and modally dismiss to ViewD.
First of all, some corrections: those are not views but view controllers. And "view A" is not pushed into the UINavigationController but it's the root.
After that, I suggest making the segue in "view C" an unwind segue and implement the IBAction in "view A" by pushing "view B" with [[self navigationController] pushViewController:bViewController animated:NO].
EDIT (adding some details):
I assume that in ViewControllerA's viewWillAppear you present ViewControllerC in a not animated manner.
Implement an unwinding action like (IBAction)unwindAndThenGoToB:(UIStoryboardSegue *)segue in ViewControllerA.
In the storyboard connect the button in ViewControllerC to the Exit icon and select the previously defined method.
Then implement the method with the push call I wrote earlier.
ps: for documentation there is plenty on Apple's website.
Implement this using delegates.Decalre protocol in which class you want and define those methods and call the methods in the view controller you want.There is no many ways of calling some view and showing back button to go different view.modal view is just a concept.and you can use delegate methods to call whatever class you want.
Here I got a way to do so:-
You need to set no animation for segue from viewC to viewA as shown in below image. Then set a segue identifier for segue from viewA to viewB namely, "viewB" and in your viewA .m file add following code,
- (void)viewDidLoad {
[super viewDidLoad];
// Place your conditional check here.
[self performSegueWithIdentifier:#"viewB" sender:self]; //Will directly lead to viewB and viewA won't be shown as no animation is there from viewC to viewA.
}
And your rest flow be like-wise.
I found the solution myself.
First, I discovered that, my original proposal of
In the viewDidLoad method of View Controller A, I check the variable
'directlyProceedToViewB' and if it is YES, I call
'performSegueWithIdentifier' to segue to View B
works as I desired on iOS 7 but does not work on iOS 8.
So the solution is, in the viewDidLoad method of View Controller A, if 'directlyProceedToViewB' is YES, rather than calling performSegueWithIdentifier, use the following code:
ViewControllerB *destVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewControllerBStoryboardID"];
[self.navigationController pushViewController:destVC animated:NO];

UINavigationController back button doesn't pop to previous VC

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.

Go forward in UINavigationController

I have 2 views, A and B. A is a tableview, and B is a detail view. When a cell is tapped in view A, the appropriate data is loaded in view B and I use [self.navigationcontroller pushviewcontroller] to present it. This all works fine, but if the user presses row 10, lets view B load, goes back, and presses row 10 again, I would like for it to just go forward to the view B that is already loaded. The navigationcontroller always lists as only having one view in the stack. Do I have to manually save a view to the navigationcontroller to re-present? The project uses ARC and Storyboard if that makes a differance. Thanks
For this actually you dont even have to concern yourself with navigationcontroller. Have a UIViewController variable (lets say lastViewController) in your ViewController1 where viewA and viewB reside.
Before pushing ViewController2 the detailed view store that reference in lastViewController. When you pop back and press another row, check if its that same viewController and show the same viewController.
But I would advise against this approach. Keep it simple, let ViewControllerB load again. There could be a scenario where loading again is preferential as it might show the user more latest information row10.
The behavior you are seeing is correct! When ViewController B is needed it is created and pushed onto the stack of the navigation controller and we see view B. When the user goes back to ViewController A and we see view A, ViewController B is no longer needed, and it and its view go out of existence.
This is efficient and lightweight and makes perfect sense. Once you are back in ViewController A and view A, who knows what row the user will tap now? If the user taps row 10 and goes back to A, taps row 10 and goes back to A, over and over, a new ViewController B will be created and go out of existence each time. So what? From the user's point of view it is perfectly consistent; it looks like the same view B! That is all that matters. You don't want to go back to the same view B; you want to create view B all over again. And that is what you do.
Indeed, this is the genius of iOS. With a single tiny screen, it is able to make views come and go without burdening memory and other resources.
For more about UINavigationController architecture, which is what you're using here (master-detail), see my book: http://www.apeth.com/iOSBook/ch19.html#_navigation_controllers

UIStoryboard popViewController

I'm using a UISegue in my application to automatically transition between viewcontrollers in my application. I'll call them A and B.
It worked as expected, however when I wanted to pop-back to the A from B, I attempted to call
[self.navigationController popViewController] however the B's navigationController property reports null.
As a second attempt I attempted to map a button, to a UISegue back to view controller A.
However this just creates a new ViewController.
As a work around, I ended up doing as a work around was to retrieve the B viewcontroller from the UIStoryboard and calling [A.navigationController pushViewController:B]
At which point, calling [B.navigationController popViewController] worked as expected.
This seems wrong, from a storyboard segue how can I return to the previous view controller?
I don't know about the class of your A and B controllers (whether UIViewController, UITableViewController or UINavigationController), but if you follow the following pattern, it should work.
In an empty storyboard, insert a UINavigationController. This will bring in two windows to the storyboard, linked with an arrow (a segue). The one on the right should be controller A.
In A, let's say, you add a button. The button will push B into the navigation stack.
Then, you add the second controller B, and drag from the button in controller A to controller B, and choose "push" from the popped menu.
If you only use a UIViewController (A) and push B, there is no navigationController to take care of popping.
Hope that help.

Resources