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:
Related
I am making an iOS app where I want to present a flow of pages like this:
Basically I want to achieve is to have this flow of pages:
PageA
PageB
PageC
PageD, dismiss back to:
PageC
PageD
PageE, dismiss back to:
PageA (starting point, start over again)
I am using ShowViewcontroller to present the pages (modal) and DismissViewcontroller to dismiss.
As per Apple's documentation if I dismiss a VC early in the stack all subsequent UIViewCOntroller are dismissed too (Apple doc).
However I experience that ViewWillAppear and ViewDidAppear are fired on the UIViewController that are dismissed even when they do not appear (e.g. in the example when dismissing back to PageA from PageE then ViewWillAppear is called on PageD, PageC, PageB too).
This does not seem logical to me. Can anyone explain why this is happening? And perhaps correct me if I am approaching this the wrong way.
I am using Xamarin.iOS.
Apple doc:
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
The ViewControllers work with a stack. Whenever a new ViewController (of any type) is added to the stack, You lose more and more control of your ViewControllers (especially when using a modal for your ViewControllers). So, say you have 5 ViewControllers in your stack (A, B, C, D, E, as per your example), and assume they are created in the order as stated, in order to return from ViewController E to ViewController A, you'd have to go through the entire stack. That means that every ViewController in your way needs to be displayed first, in order to dismiss is (since you already have ViewController E displayed, this doesn't occur here).
I hope this helps you. Good luck!
Love and regards,
Björn
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];
The app I am developing has five view controllers.
Lets call them A, B, C, D, and E, with A as root view controller.
A will have four buttons to navigate to each of the other view controllers. Similarly, B, C, D, and E will have four buttons each to navigate to all other view controllers.
Is it a good idea to to use presentViewController: to implement the navigation, since there is no clear hierarchy in the relationship of the view controllers? I don't think I clearly understand the presented vs presenter relationship.
Does the dismissal of the presented view controller have to be handled by the presenter?
Suppose A presents B, and B then presents C, and C then presents A. Are any of the controllers released/dismissed? Who is handling whose dismissal? When A is finally presented, is B still in the memory?
The other approach I thought of is to design and write a View Controller Container and manage all the view controllers. I have read this is not an easy territory to walk on.
Which of these makes more sense?
You can use UINavigationController to push new controllers to the stack. If you don't want user to go back (it means not keeping the old view controllers in the stack) you can simply set the newly allocated view controller as the root vc:
-(void)buttonAPressed:(UIButton *)button {
AViewController *vc = [[AViewController alloc] init];
self.view.window.rootViewController = vc;
}
if your situation is that you have 5 instances of view controllers and you use just the same, you have to use the container solution. If you use the same instance, you have in each time allocated just this 5 and so you haven't problem.
If instead, you need to navigate in depth with new instance, and after came back (like a navigation controller) the best solution is instantiate each time a new view controller and when you came back to the previous, the last is dismissed.
N.B. If you need to open many instance in depth but potentially without come back (like a navigation controller) you need to use the first solution, because you have to use 1 instance for each view controller.
Here is my issue.
The App I am creating has non-linear navigation.
So I am implementing my own back button and doing my own navigation.
However, I am wondering how I should be presenting the next view.
If my navigation was linear, I could do:
-(IBAction)btnBackPressed:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
However that will not work for me since pressing back may not necessarily bring you back to the previous view.
Therefore I am thinking of using for example:
AddTaskViewController *add = [[AddTaskViewController alloc] init];
[self presentViewController:add animated:YES completion:nil];
The reason I am not using Storyboard is because all my UI is made programmatically in code.
The worry I have with this is that I think presentViewController will just push the new view on top of a stack. Thus, if the user presses back, forth, back forth, he will eventually run out of memory.
Given my circumstance that I need non-linear navigation and all my UI is created in code, what should I use to present the next view without wasting memory?
Thanks
If you are still interested in a clean solution using UINavigationController, consider this design.
Situation: Let A and B be types of view controllers. A is the root, and B is a detail view which can push or pop to other B controllers.
Goal: We want to delete any B controllers that are not adjacent to the currently presented view controller, but maintain the hierarchy so we can recreate the views when necessary. Thus, the maximum hierarchy the navigation controller will know about is A--B--B.
Design: Make A the navigation controller's delegate. Give it an array of model objects which represent B controllers enough to recreate the views from them. Add to this array whenever a B controller is pushed, which A will know about from the navigation controller delegate methods. Remove objects from the stack when a B controller is popped.
On pushing a B controller, the A controller will take the navigation controller's view controller stack and (if it exists) remove the B controller directly before the one that was displayed before the push. On popping a B controller, the A controller will (if it exists) recreate the B controller directly before the destination controller and insert it in the stack.
Example: Let's say A has kept track of a hierarchy like this: A--B1--B2--B3--B4. By the system outlined above, the navigation controller only knows about A--B3--B4. When the user pops B4, the A controller will be notified and recreate B2, inserting it before B3. Thus, the new hierarchy is A--B2--B3. When B5 is pushed from B3, B2 is removed to produce A--B3--B5.
I realize this is a fairly complicated system to implement, so I would only recommend it if you had a large number of controllers and required the amenities of UINavigationController. Another solution that occurs to me is UIPageViewController, which allows you to provide view controllers on the fly.
Hope this helps!
Yes you are correct that the user will eventually run out of memory if you keep presenting Modal view controllers using
[self presentViewController:add animated:YES completion:nil];
and
[self dismissViewControllerAnimated:YES completion:nil];
will only dismiss the top most viewController being displayed.
Instead you would be better off building your own container view controller and handle your own navigation. For more information read the "Implementing a Container View Controller" section in the UIViewController Apple documentation.
https://developer.apple.com/library/ios/Documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html
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.